release: promote Android foundation FND-002 - #14
Conversation
Merge the verified FND-002 Android foundation implementation after all hosted checks pass.
|
Warning Review limit reached
Next review available in: 7 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe Android foundation now uses account/workspace-scoped Room queues, WorkManager synchronization, device-key encryption, runtime dependency wiring, and durable capture drafts. Tests and documentation cover isolation, retries, activity recreation, security boundaries, and foundation verification. ChangesAndroid foundation
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant CaptureScreen
participant LocalStorePort
participant SyncScheduler
participant SyncWorker
participant SyncTransport
CaptureScreen->>LocalStorePort: Enqueue capture mutation
SyncScheduler->>SyncWorker: Run scoped WorkManager task
SyncWorker->>LocalStorePort: Load pending mutations
SyncWorker->>SyncTransport: Send scoped request and mutations
SyncTransport-->>SyncWorker: Return accepted, retryable, or rejected result
SyncWorker->>LocalStorePort: Delete accepted mutations
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
🧹 Nitpick comments (7)
apps/android/app/src/test/java/com/databreeze/android/BoundaryTest.kt (1)
54-81: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd the workspace dimension to this isolation test.
The test name states "account or workspace", but both scopes use
workspace-1and differ only by account. The workspace half of the composite key is untested here.RoomIsolationTesthas the same gap: it varies onlyaccountIdand then queries a workspace that was never populated.Add a third scope such as
AccountWorkspaceScope("account-1", "workspace-2"), enqueue the samemutation-1, and assert thatclear(first)leaves it intact.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/android/app/src/test/java/com/databreeze/android/BoundaryTest.kt` around lines 54 - 81, Extend in_memory_store_cannot_cross_account_or_workspace_boundaries with a third scope using the same account as first but a different workspace, enqueue its mutation-1, and assert it remains after clear(first). Apply the same workspace-dimension coverage to RoomIsolationTest, ensuring the queried workspace is populated and remains isolated.apps/android/app/src/main/java/com/databreeze/android/storage/LocalStore.kt (2)
185-192: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAlign the fake's emission and ordering with
RoomLocalStore.Two behavior differences exist between this fake and the Room implementation:
observeQueuefilters after aStateFlow, so a mutation in another scope emits an unchanged list to this scope's collectors. AdddistinctUntilChanged()to match Room's per-scope emission behavior.snapshotQueuereturnsLinkedHashMapinsertion order. The DAO orders bycreatedAtEpochMs ASC, mutationId ASC. Once callers set real timestamps, tests that pass against the fake can fail against Room.♻️ Proposed refactor
+import kotlinx.coroutines.flow.distinctUntilChanged + override fun observeQueue(scope: AccountWorkspaceScope): Flow<List<SyncQueueEntity>> = updates.asStateFlow().map { values -> - values.filter { it.accountId == scope.accountId && it.workspaceId == scope.workspaceId } - } + values.filter { it.accountId == scope.accountId && it.workspaceId == scope.workspaceId } + .sortedWith(compareBy({ it.createdAtEpochMs }, { it.mutationId })) + }.distinctUntilChanged() override suspend fun snapshotQueue(scope: AccountWorkspaceScope): List<SyncQueueEntity> = mutex.withLock { items.values.filter { it.accountId == scope.accountId && it.workspaceId == scope.workspaceId } + .sortedWith(compareBy({ it.createdAtEpochMs }, { it.mutationId })) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/android/app/src/main/java/com/databreeze/android/storage/LocalStore.kt` around lines 185 - 192, Update observeQueue in LocalStore to apply distinctUntilChanged after filtering so collectors emit only when their scoped queue contents change. Update snapshotQueue to sort matching items by createdAtEpochMs ascending, then mutationId ascending, matching RoomLocalStore’s DAO ordering instead of relying on LinkedHashMap insertion order.
109-117: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove the
completedStateparameter frommarkCompleted.The Kotlin default argument works at the call site. Room receives the explicit bind parameter. However, callers can pass any
Stringand write an invalidstate. BindSyncQueueEntity.COMPLETED_STATEdirectly and remove the parameter.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/android/app/src/main/java/com/databreeze/android/storage/LocalStore.kt` around lines 109 - 117, Update the Room query and markCompleted method in LocalStore so the SET clause binds SyncQueueEntity.COMPLETED_STATE directly. Remove the completedState parameter while preserving the existing accountId, workspaceId, mutationId filters and return type.apps/android/app/build.gradle.kts (1)
94-98: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winPrefer KSP and the Room Gradle plugin for Room 2.7.2.
Replace the
kaptdependency and arguments with KSP androom { schemaDirectory(...) }. Move the existing schema file into the plugin’s variant-specific schema directory. The current$projectDir/schemaspath is correct. Ifkaptremains, use the existinglibs.plugins.kotlin.kaptalias instead of the raw plugin ID.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/android/app/build.gradle.kts` around lines 94 - 98, Replace the Room configuration in the kapt block with KSP and the Room Gradle plugin for Room 2.7.2, using room { schemaDirectory("$projectDir/schemas") } and the appropriate plugin aliases. Move the existing schema file into the plugin-generated variant-specific schema directory, preserving the current projectDir/schemas root; if kapt is still required elsewhere, apply libs.plugins.kotlin.kapt rather than a raw plugin ID.apps/android/app/src/test/java/com/databreeze/android/AndroidRuntimeContractTest.kt (1)
35-51: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valueImport
androidx.work.Datadirectly inAndroidRuntimeContractTest.kt.Data.Builder.build()is suitable for JVM unit tests; do not add Robolectric or move these tests tosrc/androidTest.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/android/app/src/test/java/com/databreeze/android/AndroidRuntimeContractTest.kt` around lines 35 - 51, Update AndroidRuntimeContractTest to import androidx.work.Data directly and replace the fully qualified Data references with the imported symbol, while keeping these JVM unit tests in place and continuing to use Data.Builder.build() without adding Robolectric.apps/android/app/schemas/com.databreeze.android.storage.DataBreezeDatabase/1.json (1)
1-79: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winRegenerate the Room schema output and commit it unchanged.
The
identityHashmatchesSyncQueueEntity. Missing emptyforeignKeysandviewsarrays do not causeMigrationTestHelperto fail, but they make the file differ from canonical Room output.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/android/app/schemas/com.databreeze.android.storage.DataBreezeDatabase/1.json` around lines 1 - 79, Regenerate the Room schema JSON for the database containing SyncQueueEntity using the project’s standard Room schema export process, ensuring the canonical empty foreignKeys and views arrays are included. Commit the generated schema output unchanged, preserving the existing identityHash and database definition.tools/repo-cli/test/execution-orchestration.test.mjs (1)
179-193: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winAssert the feature pull request checkpoint.
This test does not validate
ledger.checkpoint.lastFeaturePullRequest. Add an assertion for feature PR#13so future ledger updates cannot retain the stale#12value.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tools/repo-cli/test/execution-orchestration.test.mjs` around lines 179 - 193, Extend the ledger assertions in the execution orchestration test to verify that ledger.checkpoint.lastFeaturePullRequest equals 13. Keep the existing task-state and commit validations unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@apps/android/app/src/androidTest/java/com/databreeze/android/MainActivityTest.kt`:
- Around line 27-33: Update MainActivityTest’s draft save verification to wait
for the saved-state semantics represented by R.string.capture_saved, rather than
merely the always-present draft-status tag. Assert that saved state before
activityRule.scenario.recreate(), then repeat the same saved-state assertion
afterward while retaining the existing screen visibility checks.
In
`@apps/android/app/src/androidTest/java/com/databreeze/android/RoomIsolationTest.kt`:
- Around line 33-37: Update
apps/android/app/src/androidTest/java/com/databreeze/android/RoomIsolationTest.kt#L33-L37
to enqueue a copy of first with workspaceId set to workspace-b and assert
snapshot("account-a", "workspace-b") returns exactly that row. Update
apps/android/app/src/test/java/com/databreeze/android/BoundaryTest.kt#L54-L81 to
add AccountWorkspaceScope("account-1", "workspace-2"), enqueue the same
mutation-1 for that scope, and verify clear(first) leaves the workspace-2 row
intact, covering workspace isolation in both tests.
In `@apps/android/app/src/main/java/com/databreeze/android/AndroidRuntime.kt`:
- Around line 24-27: Update signOut and the SyncWorker/SyncTransport flow to
persist and enforce a revocation guard before any transport begins, so workers
that already snapshotted the queue cannot send after sign-out clears the scope
and deletes the key. Do not rely solely on syncScheduler.cancel or its
cancellation Operation; check the guard immediately before synchronization and
abort revoked work. Add a WorkManager test covering the race between queue
snapshot and signOut.
In `@apps/android/app/src/main/java/com/databreeze/android/MainActivity.kt`:
- Around line 46-47: Update the composition root around DataBreezeApp and
CaptureScreen to pass application.runtime.syncScheduler alongside localStore and
localScope, then invoke SyncScheduler.enqueue(localScope) in the capture save
handler immediately after localStore.enqueue completes. Preserve the existing
mutation flow and ensure scheduling occurs only after the queued capture is
durable.
In
`@apps/android/app/src/main/java/com/databreeze/android/security/DeviceKeyStore.kt`:
- Around line 72-77: Update EncryptedPayload so equality and hashing compare iv
and ciphertext contents rather than ByteArray identity, either by implementing
content-based equals/hashCode or by changing it from a data class to a class
with those overrides. Preserve the existing constructor validation for the
12-byte IV and non-empty ciphertext.
- Around line 83-89: Update DeviceKeyStore.encrypt to initialize AES/GCM
encryption without a caller-supplied GCMParameterSpec, then capture the
generated IV from cipher.iv when constructing EncryptedPayload. Remove the
now-unused random generator and its import, while preserving use of the stored
payload IV during decryption.
In `@apps/android/app/src/main/java/com/databreeze/android/storage/LocalStore.kt`:
- Around line 56-57: Remove the default value from
SyncQueueEntity.createdAtEpochMs so callers must provide it explicitly, then
update every enqueue call site to pass the injected clock’s current millisecond
value while preserving the DAO’s chronological ordering.
In `@apps/android/app/src/main/java/com/databreeze/android/sync/SyncPorts.kt`:
- Around line 123-135: Update doWork around SyncTransportResult.Accepted so
accepted mutations are removed from sync_queue (or purge completed rows) and the
entire batch status update/removal executes atomically in one Room transaction.
Preserve retry and rejection handling, and confirm the mutation-ID-only
SyncRequest matches the intended foundation scope without adding payload
handling unless required.
- Around line 99-103: Update the enqueueUniqueWork call in SyncPorts to replace
ExistingWorkPolicy.KEEP with an intentional policy that preserves the newest
cursor and revision, preferably APPEND_OR_REPLACE for ordered execution or
REPLACE for newest-input semantics. Keep the existing cursor and revision
request data unchanged.
- Around line 139-151: Remove the WorkManagerInitializer metadata from the
application manifest so the default App Startup initialization is disabled.
Preserve DataBreezeApplication’s runtime.workerFactory configuration, ensuring
WorkManager instantiates SyncWorker through DataBreezeWorkerFactory.
In `@docs/plans/execution-orchestration.json`:
- Around line 18-22: Update docs/plans/execution-orchestration.json lines 18-22
so lastFeaturePullRequest is 13, and update
tools/repo-cli/test/execution-orchestration.test.mjs lines 179-193 to assert
ledger.checkpoint.lastFeaturePullRequest equals 13.
In `@tools/repo-cli/test/android-shell.test.mjs`:
- Around line 43-60: Strengthen the Android manifest assertions in the test by
parsing or structurally matching component opening elements, so an exported
attribute is evaluated only on the relevant component and exported services are
detected regardless of attribute order. Update the backup and extraction rule
checks to require each protected domain on an exclude element, rather than
accepting include elements with matching domain tokens.
---
Nitpick comments:
In `@apps/android/app/build.gradle.kts`:
- Around line 94-98: Replace the Room configuration in the kapt block with KSP
and the Room Gradle plugin for Room 2.7.2, using room {
schemaDirectory("$projectDir/schemas") } and the appropriate plugin aliases.
Move the existing schema file into the plugin-generated variant-specific schema
directory, preserving the current projectDir/schemas root; if kapt is still
required elsewhere, apply libs.plugins.kotlin.kapt rather than a raw plugin ID.
In
`@apps/android/app/schemas/com.databreeze.android.storage.DataBreezeDatabase/1.json`:
- Around line 1-79: Regenerate the Room schema JSON for the database containing
SyncQueueEntity using the project’s standard Room schema export process,
ensuring the canonical empty foreignKeys and views arrays are included. Commit
the generated schema output unchanged, preserving the existing identityHash and
database definition.
In `@apps/android/app/src/main/java/com/databreeze/android/storage/LocalStore.kt`:
- Around line 185-192: Update observeQueue in LocalStore to apply
distinctUntilChanged after filtering so collectors emit only when their scoped
queue contents change. Update snapshotQueue to sort matching items by
createdAtEpochMs ascending, then mutationId ascending, matching RoomLocalStore’s
DAO ordering instead of relying on LinkedHashMap insertion order.
- Around line 109-117: Update the Room query and markCompleted method in
LocalStore so the SET clause binds SyncQueueEntity.COMPLETED_STATE directly.
Remove the completedState parameter while preserving the existing accountId,
workspaceId, mutationId filters and return type.
In
`@apps/android/app/src/test/java/com/databreeze/android/AndroidRuntimeContractTest.kt`:
- Around line 35-51: Update AndroidRuntimeContractTest to import
androidx.work.Data directly and replace the fully qualified Data references with
the imported symbol, while keeping these JVM unit tests in place and continuing
to use Data.Builder.build() without adding Robolectric.
In `@apps/android/app/src/test/java/com/databreeze/android/BoundaryTest.kt`:
- Around line 54-81: Extend
in_memory_store_cannot_cross_account_or_workspace_boundaries with a third scope
using the same account as first but a different workspace, enqueue its
mutation-1, and assert it remains after clear(first). Apply the same
workspace-dimension coverage to RoomIsolationTest, ensuring the queried
workspace is populated and remains isolated.
In `@tools/repo-cli/test/execution-orchestration.test.mjs`:
- Around line 179-193: Extend the ledger assertions in the execution
orchestration test to verify that ledger.checkpoint.lastFeaturePullRequest
equals 13. Keep the existing task-state and commit validations unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 82d15fdf-1cc0-49ec-9b67-9683ccd2c814
📒 Files selected for processing (19)
apps/android/README.mdapps/android/app/build.gradle.ktsapps/android/app/schemas/com.databreeze.android.storage.DataBreezeDatabase/1.jsonapps/android/app/src/androidTest/java/com/databreeze/android/MainActivityTest.ktapps/android/app/src/androidTest/java/com/databreeze/android/RoomIsolationTest.ktapps/android/app/src/main/java/com/databreeze/android/AndroidRuntime.ktapps/android/app/src/main/java/com/databreeze/android/DataBreezeApplication.ktapps/android/app/src/main/java/com/databreeze/android/MainActivity.ktapps/android/app/src/main/java/com/databreeze/android/security/DeviceKeyStore.ktapps/android/app/src/main/java/com/databreeze/android/storage/LocalStore.ktapps/android/app/src/main/java/com/databreeze/android/sync/SyncPorts.ktapps/android/app/src/test/java/com/databreeze/android/AndroidRuntimeContractTest.ktapps/android/app/src/test/java/com/databreeze/android/BoundaryTest.ktapps/android/app/src/test/java/com/databreeze/android/SyncSchedulerContractTest.ktapps/android/gradle/libs.versions.tomldocs/operations/foundation-android-2026-08-02.mddocs/plans/execution-orchestration.jsontools/repo-cli/test/android-shell.test.mjstools/repo-cli/test/execution-orchestration.test.mjs
| class DataBreezeWorkerFactory( | ||
| private val store: LocalStorePort, | ||
| private val transport: SyncTransport, | ||
| ) : WorkerFactory() { | ||
| override fun createWorker( | ||
| appContext: Context, | ||
| workerClassName: String, | ||
| workerParameters: WorkerParameters, | ||
| ): ListenableWorker? = when (workerClassName) { | ||
| SyncWorker::class.qualifiedName -> SyncWorker(appContext, workerParameters, store, transport) | ||
| else -> null | ||
| } | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify custom WorkManager initialization for the Android app.
set -euo pipefail
fd -t f 'DataBreezeApplication.kt' apps/android --exec cat -n {}
fd -t f 'AndroidManifest.xml' apps/android --exec cat -n {}
echo "== Configuration.Provider / workerFactory references =="
rg -nP --type=kt -C 3 'Configuration\.Provider|workerFactory|WorkManager\.initialize'
echo "== Default initializer removal =="
rg -n -C 3 'WorkManagerInitializer|androidx.startup' -g '*.xml'Repository: DatabreezeService/databreeze-platform
Length of output: 2119
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== WorkManager and startup declarations =="
rg -n -C 4 'androidx\.work|work-runtime|WorkManagerInitializer|InitializationProvider|androidx\.startup' \
--glob '!*build*' --glob '!node_modules/**' .
echo "== Android manifests =="
fd -t f -i 'AndroidManifest.xml' apps/android --exec sh -c 'echo "--- $1"; cat -n "$1"' sh {}
echo "== Application and runtime factory wiring =="
fd -t f 'DataBreezeApplication.kt' apps/android --exec sh -c 'echo "--- $1"; cat -n "$1"' sh {}
rg -n -C 5 'workerFactory|DataBreezeWorkerFactory|WorkManager\.getInstance|Configuration\.Provider|workManagerConfiguration' \
apps/android --glob '*.kt' --glob '*.java'Repository: DatabreezeService/databreeze-platform
Length of output: 17093
🌐 Web query:
AndroidX WorkManager Configuration.Provider automatic initialization remove WorkManagerInitializer manifest custom WorkerFactory documentation
💡 Result:
To configure a custom WorkerFactory in WorkManager using Configuration.Provider, you must first disable the default WorkManager automatic initialization [1][2]. 1. Disable Automatic Initialization Since WorkManager 2.6+, it uses the App Startup library internally [1][3]. To disable it, modify your AndroidManifest.xml to remove the WorkManagerInitializer meta-data node from the InitializationProvider [1][3]. Add the following to your AndroidManifest.xml: 2. Implement Configuration.Provider Have your Application class implement the Configuration.Provider interface [1][2]. This allows WorkManager to discover your configuration on-demand when you call WorkManager.getInstance(Context) [1][2]. class MyApplication: Application, Configuration.Provider { override fun getWorkManagerConfiguration: Configuration { return Configuration.Builder.setWorkerFactory(MyCustomWorkerFactory).build } } 3. Important Considerations - Accessing WorkManager: Always use WorkManager.getInstance(Context) to access the singleton instance [1][2]. - Timing: On-demand initialization may delay certain features like automatic rescheduling of work after a crash or device reboot [2][4]. - WorkerFactory: When implementing a custom WorkerFactory, return null if your factory cannot create the requested worker, allowing WorkManager to delegate to its default factory [5]. - Manual Initialization: If you prefer not to use Configuration.Provider, you can manually initialize WorkManager by calling WorkManager.initialize(context, configuration) in your Application.onCreate, but you must still remove the default initializer from the manifest to avoid an IllegalStateException [1][6][7].
Citations:
- 1: https://developer.android.com/develop/background-work/background-tasks/persistent/configuration/custom-configuration
- 2: https://developer.android.com/reference/androidx/work/Configuration.Provider
- 3: https://developer.android.google.cn/develop/background-work/background-tasks/persistent/configuration/custom-configuration
- 4: https://developer.android.com/reference/kotlin/androidx/work/Configuration.Provider
- 5: https://developer.android.com/reference/kotlin/androidx/work/WorkerFactory
- 6: https://developer.android.google.cn/reference/kotlin/androidx/work/WorkManager
- 7: https://stackoverflow.com/questions/55875198/illegalstateexception-workmanager-is-already-initialized
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Declared WorkManager version =="
fd -t f 'libs.versions.toml' apps/android --exec rg -n -C 3 '^work\s*=|androidx-work-runtime' {}
echo "== WorkManager references in dependency configuration =="
fd -t f apps/android --exec rg -n -C 2 'libs\.androidx\.work|androidx-work-runtime|work-runtime-ktx' {} \; 2>/dev/null || trueRepository: DatabreezeService/databreeze-platform
Length of output: 1149
🌐 Web query:
site:developer.android.com/develop/background-work/background-tasks/persistent/configuration/custom-configuration WorkManagerInitializer Configuration.Provider remove
💡 Result:
To remove the default WorkManagerInitializer and provide a custom Configuration, you must update your AndroidManifest.xml file using the tools:node="remove" merge rule [1]. Since WorkManager 2.6, WorkManager uses the App Startup library internally [1]. To disable the default initialization, add the following to your manifest: If you are using other components that rely on App Startup and do not want to remove the entire provider, you can remove only the WorkManagerInitializer node: After removing the default initializer, you should implement the Configuration.Provider interface in your Application class to provide your custom configuration [1]. WorkManager will then call your getWorkManagerConfiguration method automatically when you access WorkManager via WorkManager.getInstance(Context) [1]. Note: For versions of WorkManager older than 2.6, you would remove the workmanager-init provider instead [1].
Citations:
🌐 Web query:
androidx.work WorkManagerInitializer source Configuration.Provider getWorkManagerConfiguration automatic initialization
💡 Result:
WorkManager provides an automatic initialization mechanism suitable for most applications, which ensures the WorkManager singleton is set up before your code runs [1][2]. This is achieved using a built-in ContentProvider and, in versions 2.6.0 and later, the androidx.startup library via the WorkManagerInitializer class [2][3]. If you need to provide a custom configuration (e.g., to customize the Executor or use a WorkerFactory), you must disable this automatic initialization and either manually initialize WorkManager or use the Configuration.Provider interface [2][4][5]. Steps to customize WorkManager initialization: 1. Disable automatic initialization: Update your AndroidManifest.xml to remove the WorkManagerInitializer node [2][4]. 2. Choose one of two initialization methods: Method A: Implement Configuration.Provider (On-demand initialization) Implement the Configuration.Provider interface in your Application class and override getWorkManagerConfiguration to return your custom configuration [4][6][5]. When you access WorkManager using WorkManager.getInstance(context), it will use your provider to initialize itself [4][5]. class MyApplication: Application, Configuration.Provider { override fun getWorkManagerConfiguration: Configuration = Configuration.Builder.setMinimumLoggingLevel(android.util.Log.INFO).build } Method B: Manual Initialization After disabling the default initializer, manually call WorkManager.initialize(context, configuration) within your Application.onCreate or a ContentProvider.onCreate method [2][5]. Note that once initialized, WorkManager cannot be re-initialized with a different configuration; doing so will result in an exception [5].
Citations:
- 1: https://developer.android.com/reference/androidx/work/WorkManager
- 2: https://developer.android.com/develop/background-work/background-tasks/persistent/configuration/custom-configuration
- 3: https://developer.android.com/reference/androidx/work/WorkManagerInitializer
- 4: https://developer.android.google.cn/reference/kotlin/androidx/work/Configuration.Provider
- 5: https://medium.com/androiddevelopers/customizing-workmanager-fundamentals-fdaa17c46dd2
- 6: https://developer.android.com/reference/androidx/work/Configuration.Provider
Remove the default WorkManager initializer
DataBreezeApplication already provides runtime.workerFactory. WorkManager 2.10.3 still uses the App Startup initializer unless the manifest removes it. Remove the WorkManagerInitializer metadata so SyncWorker uses DataBreezeWorkerFactory; otherwise sync work cannot be instantiated.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/android/app/src/main/java/com/databreeze/android/sync/SyncPorts.kt`
around lines 139 - 151, Remove the WorkManagerInitializer metadata from the
application manifest so the default App Startup initialization is disabled.
Preserve DataBreezeApplication’s runtime.workerFactory configuration, ensuring
WorkManager instantiates SyncWorker through DataBreezeWorkerFactory.
fix: address promotion PR #14 review findings
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/android/app/src/main/java/com/databreeze/android/sync/SyncPorts.kt`:
- Around line 91-129: Replace the single shared mutex in both
SharedPreferencesSyncRevocationGuard and InMemorySyncRevocationGuard with
mutexes keyed by AccountWorkspaceScope.stableKey. Ensure withPermit and revoke
for the same scope continue to serialize, while operations and revocations for
different scopes use independent locks and do not block each other.
- Around line 86-89: Extend SyncRevocationGuard with reactivate(scope),
implement it in SharedPreferencesSyncRevocationGuard by clearing the persisted
revoked-${scope.stableKey} flag, and implement equivalent clearing in
InMemorySyncRevocationGuard. Invoke reactivate after successful sign-in and key
initialization, and add coverage for the sign-out/sign-in cycle to confirm
withPermit resumes operation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: c04fc565-54b0-4518-9253-0bb569ca0017
📒 Files selected for processing (16)
apps/android/app/schemas/com.databreeze.android.storage.DataBreezeDatabase/1.jsonapps/android/app/src/androidTest/java/com/databreeze/android/MainActivityTest.ktapps/android/app/src/androidTest/java/com/databreeze/android/RoomIsolationTest.ktapps/android/app/src/main/AndroidManifest.xmlapps/android/app/src/main/java/com/databreeze/android/AndroidRuntime.ktapps/android/app/src/main/java/com/databreeze/android/MainActivity.ktapps/android/app/src/main/java/com/databreeze/android/security/DeviceKeyStore.ktapps/android/app/src/main/java/com/databreeze/android/storage/LocalStore.ktapps/android/app/src/main/java/com/databreeze/android/sync/SyncPorts.ktapps/android/app/src/test/java/com/databreeze/android/AndroidRuntimeContractTest.ktapps/android/app/src/test/java/com/databreeze/android/BoundaryTest.ktapps/android/app/src/test/java/com/databreeze/android/SyncSchedulerContractTest.ktdocs/operations/code-review-14-disposition.mddocs/plans/execution-orchestration.jsontools/repo-cli/test/android-shell.test.mjstools/repo-cli/test/execution-orchestration.test.mjs
🚧 Files skipped from review as they are similar to previous changes (10)
- tools/repo-cli/test/execution-orchestration.test.mjs
- apps/android/app/schemas/com.databreeze.android.storage.DataBreezeDatabase/1.json
- apps/android/app/src/androidTest/java/com/databreeze/android/RoomIsolationTest.kt
- apps/android/app/src/androidTest/java/com/databreeze/android/MainActivityTest.kt
- docs/plans/execution-orchestration.json
- apps/android/app/src/test/java/com/databreeze/android/AndroidRuntimeContractTest.kt
- apps/android/app/src/main/java/com/databreeze/android/MainActivity.kt
- tools/repo-cli/test/android-shell.test.mjs
- apps/android/app/src/test/java/com/databreeze/android/BoundaryTest.kt
- apps/android/app/src/main/java/com/databreeze/android/storage/LocalStore.kt
fix: scope promotion revocation guards
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/android/app/src/main/java/com/databreeze/android/AndroidRuntime.kt (1)
27-43: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy liftSerialize same-scope sign-in and sign-out.
SyncRevocationGuardserializes individual guard operations, not the complete lifecycle. IfsignInpauses aftergetOrCreate,signOutcan revoke the scope, clear local state, and delete the key.signIncan then callreactivate, leaving the scope active without its device key.Use a per-
AccountWorkspaceScopelock around both methods, or expose one atomic lifecycle operation. Add a test for this interleaving.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/android/app/src/main/java/com/databreeze/android/AndroidRuntime.kt` around lines 27 - 43, Serialize the complete signIn and signOut lifecycles per AccountWorkspaceScope so they cannot interleave between deviceKeyStore and revocation or cleanup operations. Add a per-scope lock around both AndroidRuntime.signIn and AndroidRuntime.signOut, or replace them with an equivalent atomic lifecycle operation, and add a test covering signIn pausing before reactivate while signOut runs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@apps/android/app/src/main/java/com/databreeze/android/AndroidRuntime.kt`:
- Around line 27-43: Serialize the complete signIn and signOut lifecycles per
AccountWorkspaceScope so they cannot interleave between deviceKeyStore and
revocation or cleanup operations. Add a per-scope lock around both
AndroidRuntime.signIn and AndroidRuntime.signOut, or replace them with an
equivalent atomic lifecycle operation, and add a test covering signIn pausing
before reactivate while signOut runs.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 4a353b86-aa9d-40ef-8374-706ec7ebbc5e
📒 Files selected for processing (5)
apps/android/app/src/main/java/com/databreeze/android/AndroidRuntime.ktapps/android/app/src/main/java/com/databreeze/android/sync/SyncPorts.ktapps/android/app/src/test/java/com/databreeze/android/SyncSchedulerContractTest.ktdocs/operations/code-review-14-disposition.mdtools/repo-cli/test/android-shell.test.mjs
🚧 Files skipped from review as they are similar to previous changes (2)
- tools/repo-cli/test/android-shell.test.mjs
- apps/android/app/src/main/java/com/databreeze/android/sync/SyncPorts.kt
fix: serialize Android sign-in and sign-out lifecycle
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@apps/android/app/src/test/java/com/databreeze/android/AndroidRuntimeLifecycleTest.kt`:
- Around line 47-49: Make AndroidRuntimeLifecycleTest deterministically contend
on the mutex by starting signOut with CoroutineStart.UNDISPATCHED after
keyStarted.await(), or add equivalent synchronization before releasing key
creation and asserting serialization. Update
docs/operations/code-review-14-disposition.md lines 50-56 to retain the “proves”
claim only once the test establishes contention deterministically.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d68378fa-f6de-4f2b-80ab-037f6699a0cd
📒 Files selected for processing (3)
apps/android/app/src/main/java/com/databreeze/android/AndroidRuntime.ktapps/android/app/src/test/java/com/databreeze/android/AndroidRuntimeLifecycleTest.ktdocs/operations/code-review-14-disposition.md
…est-determinism test: make Android lifecycle race coverage deterministic
Summary
devPromotion policy
devafter hosted checks; CodeRabbit was intentionally skipped there.Verification on feature PR
corepack pnpm repo:checkcorepack pnpm repo:buildcorepack pnpm infra:check(static checks; OpenTofu unavailable)Medium_Phone(AVD) - 17Release notes
verified; FND-002 is foundation evidence only.Summary by CodeRabbit