Skip to content

Conversation

@CDRussell
Copy link
Member

@CDRussell CDRussell commented Dec 15, 2025

Task/Issue URL: https://app.asana.com/1/137249556945/project/72649045549333/task/1212290226255075?focus=true

Description

Ensures sync backend is aware of fire button usage (and automatic data clearing) so duck chat deletions can be synced.

Steps to test this PR

Logcat filter:

message~:"duck AI chats cleared|recorded deletion timestamp|cleared local deletion|successfully informed sync of deleted duck ai chats|Duck AI chats deletion sync successful"

Sync disabled / fire button

  • Fresh install internal flavor from this branch
  • Visit Settings -> Data Clearing and enable Clear Duck.ai Chats toggle
  • Return to home, and tap on duck ai chat button. Type a message and send it so there is a chat history.
  • Return to home, and use the native fire button to clear data
  • Verify in the logs that you see duck AI chats cleared: true
  • Verify you see in the logs recorded deletion timestamp with a timestamp that was very recently (e.g., some milliseconds ago)
  • Verify you do not see Duck AI chats deletion sync successful (because sync isn't enabled)

Sync disabled / automatic data clearing

  • Visit Settings -> Data Clearing and tap on Automatically Clear Data... and choose Tabs, data and Duck.ai chats
  • Tap Clear on... and choose the bottom option with 5 seconds
  • Return to home, and tap on duck ai chat button. Type a message and send it so there is a chat history.
  • Background the app and wait 5 seconds
  • Verify you see in the logs recorded deletion timestamp with a timestamp that was a few seconds ago (e.g., 4.5s - 5s ago); we use the timestamp when the app goes to background in the case of automatic data clearing

Sync enabled / fire button

  • Enable sync (Settings -> Sync & Backup -> Sync and Back Up This Device)
  • Return to home, and tap on duck ai chat button. Type a message and send it so there is a chat history.
  • Return to home, and use the native fire button to clear data
  • Verify in logs you see Duck AI chats deletion sync successful
  • Verify in logs you see cleared local deletion timestamp

Smoke test sync generally

  • e.g., sync two devices and add a password; make sure it syncs across

Note

Adds end-to-end support to record, expose, and sync Duck AI chat deletions (local clear → timestamp → sync DELETE /sync/ai_chats), with engine and API extensions plus feature gating.

  • Sync Engine & API:
    • Introduce deletions pipeline: new DeletableType (DUCK_AI_CHATS), DeletableDataManager, and engine flow to process deletions before changes.
    • Extend models to SyncFeatureType and update SyncDeletionRequest/Response to use DeletableType.
    • Add endpoint DELETE /sync/ai_chats (SyncService, SyncServiceRemote, SyncApiClient.delete), including error recording/pixels generalized to SyncFeatureType.
    • Add plugin point for DeletableDataManager and tests across engine/client.
  • Duck Chat:
    • New DuckAiChatDeletionListener API and plugin point (BrowserModule).
    • WebLocalStorageManager now detects and deletes Duck AI chat keys and notifies listeners on deletion.
    • Implement listener DuckAiChatDeletionListenerImpl to capture app background timestamp and record deletions via DuckChatSyncRepository/DuckChatSyncMetadataStore.
    • DuckChatSyncDataManager surfaces pending deletions to sync and clears timestamp on success; gated by DuckChatFeature.supportsSyncChatsDeletion().
    • RealDuckChat exposes isChatSyncFeatureEnabled using DeviceSyncState.isDuckChatSyncFeatureEnabled.
  • Sync feature flags:
    • Add SyncFeature.aiChatSync() and SyncFeatureToggle.allowAiChatSync() used by AppDeviceSyncState.isDuckChatSyncFeatureEnabled(); add method to DeviceSyncState (and fake impl).
  • Tests:
    • Extensive unit tests for local storage deletion notifications, Duck Chat sync repository/manager, sync engine deletions, API client, and feature toggles.

Written by Cursor Bugbot for commit 3dfdcb3. This will update automatically on new commits. Configure here.

Copy link
Member Author

CDRussell commented Dec 15, 2025

@CDRussell CDRussell force-pushed the feature/craig/api_changes_for_syncing_duck_chat_deletions branch from 06dea1c to 1dfedc6 Compare December 15, 2025 12:16
@CDRussell CDRussell force-pushed the feature/craig/sync_deletion_of_duck_ai_chats branch from 45fe17a to dbca43a Compare December 15, 2025 12:16
@CDRussell CDRussell force-pushed the feature/craig/api_changes_for_syncing_duck_chat_deletions branch from 1dfedc6 to 90905ef Compare December 16, 2025 12:37
@CDRussell CDRussell force-pushed the feature/craig/sync_deletion_of_duck_ai_chats branch 5 times, most recently from 0d8a790 to 1a80afa Compare December 16, 2025 14:48
@CDRussell CDRussell marked this pull request as ready for review December 16, 2025 16:02
@CDRussell CDRussell force-pushed the feature/craig/sync_deletion_of_duck_ai_chats branch from 1a80afa to 6833195 Compare December 16, 2025 16:12
Copy link
Contributor

@malmstein malmstein left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven’t tested the functionality yet, just reviewed the code. I don’t understand why you need to store timestamp out of MainProcessLifecycleObserver, maybe we can chat tomorrow.

@CDRussell CDRussell force-pushed the feature/craig/sync_deletion_of_duck_ai_chats branch from 6833195 to 38e016a Compare December 17, 2025 15:14
@CDRussell CDRussell force-pushed the feature/craig/api_changes_for_syncing_duck_chat_deletions branch from 90905ef to ab4b982 Compare December 17, 2025 15:14
@CDRussell CDRussell force-pushed the feature/craig/sync_deletion_of_duck_ai_chats branch 3 times, most recently from 7f14801 to c7269ca Compare December 17, 2025 17:09
@CDRussell CDRussell force-pushed the feature/craig/sync_deletion_of_duck_ai_chats branch from c7269ca to 3b1274f Compare December 17, 2025 17:27
@CDRussell CDRussell force-pushed the feature/craig/sync_deletion_of_duck_ai_chats branch 2 times, most recently from b537d28 to 9f55739 Compare December 17, 2025 17:51
@CDRussell CDRussell force-pushed the feature/craig/sync_deletion_of_duck_ai_chats branch from 9f55739 to 7076d7e Compare December 17, 2025 17:55
@CDRussell CDRussell force-pushed the feature/craig/sync_deletion_of_duck_ai_chats branch 2 times, most recently from e779640 to 79a5707 Compare December 17, 2025 17:58
@CDRussell CDRussell requested a review from malmstein December 17, 2025 18:01
@CDRussell CDRussell changed the base branch from feature/craig/api_changes_for_syncing_duck_chat_deletions to graphite-base/7354 December 18, 2025 11:06
@CDRussell CDRussell force-pushed the feature/craig/sync_deletion_of_duck_ai_chats branch from 79a5707 to c3d194b Compare December 18, 2025 11:06
@graphite-app graphite-app bot changed the base branch from graphite-base/7354 to develop December 18, 2025 11:07
@CDRussell CDRussell force-pushed the feature/craig/sync_deletion_of_duck_ai_chats branch from c3d194b to 4becbdb Compare December 18, 2025 11:07
@CDRussell
Copy link
Member Author

I don’t understand why you need to store timestamp out of MainProcessLifecycleObserver

We want the timestamp when app is going to the background because if the automatic data clearing is used then that is the timestamp that is used for the until parameter. When the fire button is used, we use the current time as the until timestamp.

@CDRussell CDRussell force-pushed the feature/craig/sync_deletion_of_duck_ai_chats branch from 4becbdb to 29fc78f Compare December 18, 2025 18:09
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Second clearWebLocalStorage overload doesn't notify deletion listeners

The clearWebLocalStorage(shouldClearBrowserData, shouldClearDuckAiData) overload deletes duck AI data when shouldClearDuckAiData is true (lines 152-156) but does not notify the duckAiChatDeletionListeners. Only the first overload clearWebLocalStorage() (lines 76-120) was updated to track deletions with duckAiDataDeleted and notify listeners. This means duck AI chat deletions via this second code path won't be synced to other devices, breaking the sync feature for scenarios that use this overload.


Please tell me if this was useful or not with a 👍 or 👎.

app/src/main/java/com/duckduckgo/app/browser/weblocalstorage/WebLocalStorageManager.kt#L121-L161

override suspend fun clearWebLocalStorage(
shouldClearBrowserData: Boolean,
shouldClearDuckAiData: Boolean,
) {
withContext(dispatcherProvider.io()) {
val settings = androidBrowserConfigFeature.webLocalStorage().getSettings()
val webLocalStorageSettings = webLocalStorageSettingsJsonParser.parseJson(settings)
val fireproofedDomains = fireproofWebsiteRepository.fireproofWebsitesSync().map { it.domain }
domains = webLocalStorageSettings.domains.list + fireproofedDomains
keysToDelete = webLocalStorageSettings.keysToDelete.list
matchingRegex = webLocalStorageSettings.matchingRegex.list
logcat { "WebLocalStorageManager: Allowed domains: $domains" }
logcat { "WebLocalStorageManager: Keys to delete: $keysToDelete" }
logcat { "WebLocalStorageManager: Matching regex: $matchingRegex" }
val db = databaseProvider.get()
db.iterator().use { iterator ->
iterator.seekToFirst()
while (iterator.hasNext()) {
val entry = iterator.next()
val key = String(entry.key, StandardCharsets.UTF_8)
val domainForMatchingAllowedKey = getDomainForMatchingAllowedKey(key)
if (domainForMatchingAllowedKey == null && shouldClearBrowserData) {
db.delete(entry.key)
logcat { "WebLocalStorageManager: Deleted key: $key" }
} else if (shouldClearDuckAiData && DUCKDUCKGO_DOMAINS.contains(domainForMatchingAllowedKey)) {
if (keysToDelete.any { key.endsWith(it) }) {
db.delete(entry.key)
logcat { "WebLocalStorageManager: Deleted key: $key" }
}
}
}
}
}
}

Fix in Cursor Fix in Web


@CDRussell CDRussell force-pushed the feature/craig/sync_deletion_of_duck_ai_chats branch 3 times, most recently from f3e5f30 to e0b3b07 Compare December 18, 2025 20:09
Copy link
Contributor

@malmstein malmstein left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, thanks for taking the feedback @CDRussell !

@CDRussell CDRussell force-pushed the feature/craig/sync_deletion_of_duck_ai_chats branch 2 times, most recently from c651d8e to 7636a72 Compare December 19, 2025 13:39
@CDRussell CDRussell force-pushed the feature/craig/sync_deletion_of_duck_ai_chats branch from 7636a72 to 3dfdcb3 Compare December 19, 2025 13:41
override fun onStart(owner: LifecycleOwner) {
appBackgroundedTimestamp = null
logcat { "DuckChat-Sync: App came to foreground, cleared background timestamp" }
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Background timestamp lost if user foregrounds during clearing

When automatic data clearing runs while the app is backgrounded, onStart (main thread) could clear appBackgroundedTimestamp before onDuckAiChatsDeleted (IO thread) reads it. In this scenario, the deletion timestamp falls back to System.currentTimeMillis() instead of the intended background timestamp. The PR description explicitly states the background timestamp should be used for automatic clearing, so this timing issue deviates from the stated behavior.


Please tell me if this was useful or not with a 👍 or 👎.

Additional Locations (1)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants