Files
obsidian-vault/work/wiki/apple-browsers/chat-path-onboarding-tracker-blocking.md
T

22 KiB
Raw Blame History

iOS: Chat-Path Onboarding — Tracker Blocking Demo

Asana: Ship Review task 1214147157456478
Branch stack: demo-tracker-blocking-onboardingui-polishchat-path-dialog-polishsr-feedbackuti-flow
Due: 2026-05-15


PR Stack

PR Branch Review status
#4544 demo-tracker-blocking-onboarding Open — review required (Alessandro + Rachel left comments)
#4591 demo-tracker-blocking-onboarding-ui-polish Approved
#4664 demo-tracker-blocking-onboarding-chat-path-dialog-polish Approved
#4855 demo-tracker-blocking-onboarding-sr-feedback Open — no review yet
#4668 demo-tracker-blocking-onboarding-uti-flow Open — no review yet

Open Items


SR — GJ2/ Remove X button from chat-path dialogs

Source: Gary Apr 29

Fix: X (manual dismiss) button removed from "Try visiting a site!", trackers-blocked, and "Oh, before I forget…" dialogs on the chat path.

State: Done. Committed in PR #4664.


SR — GJ4/ Copy: "Next, try visiting a site!" after trying AI

Source: Gary Apr 29

Fix: "Try visiting a site!" dialog title updated to "Next, try visiting a site!" for chat-path context.

State: Done. Committed in PR #4664.


🟠 SR — GJ3/ Hide address bar and toolbar for "Try visiting a site!" step (chat-path only)

Source: Gary Apr 29, confirmed by Gary May 7 and Costas May 8: hide address bar + toolbar for AI-chat flow only so user is forced to choose from presets. Without this: user could type a search (repeating the same dialog) or switch to Duck.ai.

Fix: chromeDelegate?.setBarsHidden(true) on show, setBarsHidden(false) on dismiss. Tracked as PR #4855 — Alessandro (line 477) item above.

State: sr-feedback / uncommitted — in working tree, NOT committed.


🟠 SR — GJ7/ Exclude sync-restore / returning users from Duck.ai experiment step

Source: Alessandro May 7: if user restored data from sync, Dax dialogs are disabled — enrolling them would break the flow. Alex confirmed addressed in May 13 build comment ("currently commented out in PR").

Fix: Uncommented guard case .introDialog(isReturningUser: false) = introSteps.first in insertExperimentStepIfNeeded(). Tracked as PR #4855 — Bugbot "Returning-user guard commented out" item above.

State: sr-feedback / uncommitted — in working tree, NOT committed.


🟠 SR — GJ8/ Change "Duck.ai" to "Ask AI" on toggle choice screen (onboarding only)

Source: Gary May 6: "Lets also change Duck.ai to Ask AI in the toggle screen in onboarding. Only change it in onboarding though, not the standard toggle."

Fix: DuckAI query toggle label updated to "Ask AI" in OnboardingView+DuckAIExperimentSearchContent.swift and UserText.swift. Tracked as PR #4855 — Alessandro (UserText.swift:2375) item above.

⚠️ Alessandro's review comment says "Ask.ai" (with dot) vs GJ8 "Ask AI" (no dot) — needs product confirmation.

State: sr-feedback / uncommitted — in working tree, NOT committed.


🔴 SR — GJ6b/ "Youve got this" EOJ fails after tapping "Got it" on tracker blocking step

Source: Gary Apr 29: "This dialogue fails to appear after tapping Got it on the tracker blocking step. As a result, we also skip the subscription upsell dialogue."

This is the same bug as PR #4544 — Bugbot r3234645543 below. Fix is in didTapDismissContextualOnboardingAction on sr-feedback branch, not yet on #4544 branch.

State: 🔴 Fix exists in PR #4855 (sr-feedback), NOT in PR #4544. GitHub thread on #4544 NOT resolved.


🔴 SR — GJ1/ Animated toggle thumbnail

Source: Gary Apr 29 + May 6 confirmed in scope. Costas May 8: task 1214627423981798 created for animated assets. If not in time, defer to follow-up project. Alessandro May 10: nice-to-have, defer if needed.

State: 🔴 NOT implemented. Nice-to-have — defer to follow-up if time doesnt allow.


🔴 SR — GJ6a/ UTI: keyboard appears after choosing AI, hiding response and fire button

Source: Gary Apr 29. Pete May 7: UTI experiment participants will be excluded from this experiment; UTI issues can be fixed in follow-up before June 1. Alessandro May 7: follow-up task 1214592943696234.

State: 🔴 NOT implemented here. Scoped to separate follow-up project (UTI + experiment winner work).


🔴 SR — GJ6/ UTI new breakage (visit-site dialog, bar state)

Source: Alex May 13: "new breakage since my last fixes, will do next." Tracked in PR #4668.

State: 🔴 In progress. PR #4668 has open comment threads (see aataraxiaa + Bugbot items above).


SR — GJ5/ Add new Dax brand assets to onboarding

Source: Gary Apr 29.

State: Deferred. Costas/Alessandro agreed: out of scope for this project, addressed in follow-up.


SR — CB1/ Welcome screen copy update

Source: Costas May 8. Alessandro May 10: comes for free once task 1213967368170629 merges.

State: Covered by separate task. No action needed here.


✏️ COMMIT-1 — All working-tree changes on sr-feedback are uncommitted

State: All of the items below marked sr-feedback / uncommitted are local-only on alex/demo-tracker-blocking-onboarding-sr-feedback. None have been committed or pushed.

  • iOS/DuckDuckGo/MainViewController.swift
  • iOS/DuckDuckGo/NewTabPageControllerDelegate.swift
  • iOS/DuckDuckGo/NewTabPageViewController.swift
  • iOS/DuckDuckGo/OnboardingFlow/LinearOnboarding/OnboardingIntroViewModel.swift
  • iOS/DuckDuckGo/OnboardingView+DuckAIExperimentSearchContent.swift
  • iOS/DuckDuckGo/UserText.swift + all 26 lproj files

Action: Commit and push before #4855 can be reviewed.


🟠 PR #4855 — Alessandro (line 280): Use NewTabPageControllerDelegate instead of (parent as? MainViewController) cast

Source: #4855 review comment, line 280

Fix: Added newTabPageControllerCurrentTabIsLoading(_ :) -> Bool to NewTabPageControllerDelegate protocol; implemented in MainViewController.swift; removed the (parent as? MainViewController)? cast from NewTabPageViewController.

State: sr-feedback / uncommitted — fix is in working tree, NOT committed. GitHub thread NOT resolved.


🟠 PR #4855 — Alessandro (line 477): Hide both bars via setBarsHidden (Costas preference)

Source: #4855 review comment, line 477

Fix: Replaced chromeDelegate?.setNavigationBarHidden(true/false) + (parent as? MainViewController)?.setChatPathVisitSiteControlsLocked(true/false) with chromeDelegate?.setBarsHidden(true/false, animated: false, customAnimationDuration: nil) in NewTabPageViewController — both the show path and dismiss() / dismissHostingController(). This removes all (parent as?) casts from NewTabPageViewController.

Why setChatPathVisitSiteControlsLocked calls were removed: Alessandro's review requested setBarsHidden via chromeDelegate (no parent cast). setBarsHidden hides both address bar and toolbar in one call, making setChatPathVisitSiteControlsLocked unreachable. The method definition still sits in MainViewController+DuckAIExperiment.swift:166 as dead code — see DEAD-1 below.

State: sr-feedback / uncommitted — fix is in working tree, NOT committed. GitHub thread NOT resolved.


🟠 PR #4855 — Alessandro (line 473): Fold loading check into if condition

Source: #4855 review comment, line 473

Fix: Replaced guard (parent as? MainViewController)?.currentTab?.isLoading != true else { return } (which was a standalone guard that aborted the whole function, leaving an orphaned hostingController) with delegate?.newTabPageControllerCurrentTabIsLoading(self) != true as a third condition in the outer if. The dialog still presents; only the bar-hiding is skipped when loading.

State: sr-feedback / uncommitted — fix is in working tree, NOT committed. GitHub thread NOT resolved.


🟠 PR #4855 — Alessandro (UserText.swift:2375): Revert searchAndDuckAIOption copy

Source: #4855 review comment, UserText.swift:2375

Fix: Reverted searchAndDuckAIOption back to "Toggle between\nSearch and Duck.ai" in UserText.swift and all 26 lproj files. (Was incorrectly changed to "Ask AI" — that rename applies only to the DuckAI query toggle label, not the Search Experience screen.)

State: sr-feedback / uncommitted — fix is in working tree, NOT committed. GitHub thread NOT resolved.


🟠 PR #4855 — Bugbot: Nav bar not restored when NTP dismissed during visit-site dialog

Source: Bugbot on PR #4855, NewTabPageViewController.swift:281

Fix: dismiss() now checks didHideBarsForChatPathVisitSiteDialog and calls setBarsHidden(false) before removing the view. Previously only dismissHostingController() restored the bars, but dismiss() (called when the NTP is removed via removeHomeScreen()) did not.

State: sr-feedback / uncommitted — fix is in working tree, NOT committed. GitHub thread NOT resolved.


🟠 PR #4855 — Bugbot: Guard early return leaves orphaned hostingController and blank NTP

Source: Bugbot on PR #4855, NewTabPageViewController.swift:473

Fix: Same as the Alessandro (line 473) item above — guard replaced with if condition using delegate method.

State: sr-feedback / uncommitted — fix is in working tree, NOT committed. GitHub thread NOT resolved.


🟠 PR #4855 — Bugbot: Hardcoded return .treatmentB bypasses feature flag in OnboardingIntroViewModel

Source: Bugbot on PR #4855, OnboardingIntroViewModel.swift:473

Fix: Removed return .treatmentB (and its // TODO: Remove this comment) from resolveDuckAIQueryExperimentCohortID(). The method now always goes through featureFlagger.resolveCohort().

State: sr-feedback / uncommitted — fix is in working tree, NOT committed. GitHub thread NOT resolved.


🟠 PR #4855 — Bugbot: Returning-user guard commented out in insertExperimentStepIfNeeded

Source: Bugbot on PR #4855, OnboardingIntroViewModel.swift:451. Also: Alessandro May 7 review on #4544, Asana task 1214682678629767.

Fix: Uncommented guard case .introDialog(isReturningUser: false) = introSteps.first in insertExperimentStepIfNeeded(). Returning users (including sync-restore users) are now excluded from the Duck.ai query experiment step. The isReturningUser value comes from OnboardingManager.isNewUser (line 250 in OnboardingIntroViewModel: introDialog(isReturningUser: !isNewUser)).

State: sr-feedback / uncommitted — fix is in working tree, NOT committed. GitHub thread NOT resolved.


🔴 PR #4544 — Bugbot (r3234645543) / SR GJ6b/: Chat-path EOJ not shown after tapping tracker dialog CTA

Source: Bugbot comment r3234645543 on PR #4544, TabViewController.swift:4214. Also reported as SR GJ6b/ by Gary Apr 29 — same bug.

Issue: When user taps "Got it" on the tracker-blocked dialog, the code goes through didTapDismissContextualOnboardingAction, which does not call tabDidRequestNewTab. The EOJ new tab is never opened.

Fix: Added if contextualOnboardingLogic.chatPathPhase == .trackerToEOJ { delegate?.tabDidRequestNewTab(self) } to didTapDismissContextualOnboardingAction. (Same block was already in didNavigateAwayFromContextualOnboardingDialog.)

State: Fix exists in commit 6c3774e4f8 on sr-feedback branch (PR #4855). NOT present on the #4544 branch itself. Fix will land in main when the full PR stack merges. GitHub thread on PR #4544 is NOT resolved.


🔴 PR #4544 — Bugbot: Chat-path completion dialog shown twice via competing paths

Source: Bugbot on PR #4544, MainViewController.swift:4711

Issue: Two independent paths both call presentChatPathOnboardingCompletionIfNeeded()tabDidRequestNewTab dispatch and a direct call — potentially showing the completion dialog twice.

State: presentChatPathOnboardingCompletionIfNeeded() has a guard on chatPathPhase == .trackerToEOJ — once the EOJ fires, phase advances and the second call is a no-op. Needs manual verification. GitHub thread NOT resolved.


🔴 PR #4544 — Bugbots (outdated): hardcoded cohort / promo override / missing pixels / commented-out redirect

Source: Four Bugbot comments on early commits of PR #4544.

Comment Fix Code state
Hardcoded return .treatmentA in OnboardingIntroViewModel Removed in later commit on #4544 Fixed (Bugbot commit is outdated)
shouldDisplay always returns true in OnboardingSubscriptionPromotionHelper Reverted in commit f8d5ba2 on #4544 Fixed
Missing pixel definitions in PixelEvent.swift Added in commit ec42fd8 on #4544 Fixed
isChatPathSubscriptionPromo commented out, variable unused in RebrandedNewTabDaxDialogFactory Removed in commit e682475 on #4544 Fixed

State: All four are fixed in code. All four GitHub threads are NOT resolved (outdated Bugbot comments never closed). → Resolve all four threads.


🔴 PR #4668 — aataraxiaa: Commented-out Config(...) line in FeatureFlag.swift:635

Source: aataraxiaa review comment on PR #4668, FeatureFlag.swift:635

Issue: defaultValue: .enabled override was added to unifiedToggleInput for testing, plus the old // Config(...) line is still sitting underneath it as dead commented-out code. Both need to be removed before merging.

State: 🔴 Still in code on the #4668 branch. NOT fixed. GitHub thread NOT resolved.


🔴 PR #4668 — Bugbot: embedInUnifiedInputEditingAreaIfActive added to protocol but never called

Source: Bugbot on PR #4668, BrowserChromeManager.swift:43 and MainViewController.swift:3457

Issue: Method added to BrowserChromeDelegate protocol and implemented in BrowserChromeManager, but no caller exists anywhere.

State: On sr-feedback branch commit 82bb0f71e3 removed embedDialogInEditingState dead code; the protocol method may now be entirely unreachable. Needs verification on #4668 branch. GitHub thread NOT resolved. → Either add a caller or remove the method and its implementation.


🟠 DEAD-1 — setChatPathVisitSiteControlsLocked in MainViewController+DuckAIExperiment.swift:166 is dead code

Source: Callers removed as part of PR #4855 Alessandro (line 477) fix above.

State: Method definition still exists in MainViewController+DuckAIExperiment.swift:166, no callers. Needs deletion. Not committed.


⚠️ GJ8 / Copy: "Ask AI" vs "Ask.ai"

Source: GJ8 ship review says "Ask AI" (no dot). Alessandro's #4855 review comment says "Ask.ai" (with dot).

State: Implemented as "Ask AI" (no dot) per GJ8. ⚠️ Needs explicit confirmation from product/design before shipping.


🔴 PRE-SHIP-1 — Privacy config: add US/EN targeting

Source: Task 1214682678629768. Without targets, experiment enrolls all locales worldwide.

What: In privacy-configuration/overrides/ios-override.json, add "targets": [{ "localeLanguage": "en", "localeCountry": "US" }] to the onboardingDuckAIQueryExperiment entry.

State: Local edit exists on wrong branch (alex/remove-autocomplete-tabs) in privacy-configuration repo, not committed. Should be done together with PRE-SHIP-2 in one PR to that repo.


🔴 PRE-SHIP-2 — Privacy config: rename the experiment

Source: Task 1214682678629768. Internal testers already enrolled under onboardingDuckAIQueryExperiment; new name ensures clean production enrollment.

What (three parts):

  1. BSK — new case in AIChatSubfeature enum with the new name string
  2. App — update FeatureFlag.onboardingDuckAIQueryExperiment to reference the new subfeature case
  3. privacy-configuration repo — add new experiment entry with targets; set old entry to "state": "disabled"

State: 🔴 Blocked — new experiment name not decided.


🔴 PRE-SHIP-3 — Tell UTI team the final experiment name

Source: Pete Apr 30 comment. Pete's team needs the name to exclude enrolled users from the UTI feature rollout.

What: Fill the name into O-J <> O-N Coordination and O-N Live Onboarding Experiment Details.

State: 🔴 Blocked on PRE-SHIP-2 (name not decided).


Status Summary (mirrors deleted Asana comment, updated to current state)

These were the TBD items called out in the ship review. Status reflects current state as of May 14.

GJ6c/ No longer required. The address bar will be hidden for the "Try visiting a site" step (GJ3 / setBarsHidden). Costas confirmed a search performed after the trackers-blocked dialog is acceptable, so no additional guard is needed here.

GJ7/ Sync-restore exclusion 🟠 Implemented, not yet committed. guard case .introDialog(isReturningUser: false) = introSteps.first is now active in insertExperimentStepIfNeeded(). Users who selected "Restore My Stuff" (sync restore) will not be enrolled in the experiment. Working tree only — needs committing to sr-feedback.

GJ3/ Hide address bar + toolbar 🟠 Implemented via chromeDelegate?.setBarsHidden(true/false), not yet committed. Replaces earlier setNavigationBarHidden + setChatPathVisitSiteControlsLocked approach.

GJ8/ "Ask AI" toggle label 🟠 Implemented in OnboardingView+DuckAIExperimentSearchContent.swift, not yet committed. ⚠️ Pending copy confirmation ("Ask AI" vs "Ask.ai").

Experiment rename in privacy config 🔴 TBD. Need to create a new experiment name, add new AIChatSubfeature case in BSK, update FeatureFlag, and add new entry in privacy-configuration/overrides/ios-override.json (disabling old one). Blocked — new name not decided.

Privacy config targets payload 🔴 TBD. "targets": [{ "localeLanguage": "en", "localeCountry": "US" }] must be added to the experiment entry. Local edit exists on wrong branch in privacy-configuration repo. Do together with rename above.

O-N Live Onboarding Experiment Details (task 1214601039604921) 🔴 TBD. Provide Pete with final experiment name once rename is decided. Also fill into O-J <> O-N Coordination.


Experiment Config (current, local override only)

privacy-configuration/overrides/ios-override.json under aiChat subfeatures:

"onboardingDuckAIQueryExperiment": {
  "state": "enabled",
  "targets": [{ "localeLanguage": "en", "localeCountry": "US" }],
  "cohorts": [
    { "name": "control", "weight": 1 },
    { "name": "treatmentA", "weight": 1 },
    { "name": "treatmentB", "weight": 1 }
  ]
}

Feature flag: FeatureFlag.onboardingDuckAIQueryExperiment in iOS/Core/FeatureFlag.swift
Cohort type: FeatureFlag.DuckAIQueryExperimentCohort