# iOS: Chat-Path Onboarding — Tracker Blocking Demo > **Asana:** [Ship Review task 1214147157456478](https://app.asana.com/1/137249556945/task/1214147157456478) > **Branch stack:** `demo-tracker-blocking-onboarding` → `ui-polish` → `chat-path-dialog-polish` → `sr-feedback` → `uti-flow` > **Due:** 2026-05-15 --- ## PR Stack | PR | Branch | Review status | |---|---|---| | [#4544](https://github.com/duckduckgo/apple-browsers/pull/4544) | `demo-tracker-blocking-onboarding` | Open — review required (Alessandro + Rachel left comments) | | [#4591](https://github.com/duckduckgo/apple-browsers/pull/4591) | `demo-tracker-blocking-onboarding-ui-polish` | Approved | | [#4664](https://github.com/duckduckgo/apple-browsers/pull/4664) | `demo-tracker-blocking-onboarding-chat-path-dialog-polish` | Approved | | [#4855](https://github.com/duckduckgo/apple-browsers/pull/4855) | `demo-tracker-blocking-onboarding-sr-feedback` | Open — no review yet | | [#4668](https://github.com/duckduckgo/apple-browsers/pull/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](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214409112744728) **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](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214409112744728) **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](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214409112744728), confirmed by [Gary May 7](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214615822543276) and [Costas May 8](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214627423981810): 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](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214595287457513): 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](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214592278234254): "Let’s 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/ "You’ve got this" EOJ fails after tapping "Got it" on tracker blocking step **Source:** [Gary Apr 29](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214409112744728): "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](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214592278234254) confirmed in scope. [Costas May 8](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214627423981810): task [1214627423981798](https://app.asana.com/1/137249556945/project/1212535659026889/task/1214627423981798) created for animated assets. If not in time, defer to follow-up project. [Alessandro May 10](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214683268207876): nice-to-have, defer if needed. **State:** 🔴 NOT implemented. Nice-to-have — defer to follow-up if time doesn’t allow. --- ### 🔴 SR — GJ6a/ UTI: keyboard appears after choosing AI, hiding response and fire button **Source:** [Gary Apr 29](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214409112744728). [Pete May 7](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214600831548835): UTI experiment participants will be excluded from this experiment; UTI issues can be fixed in follow-up before June 1. [Alessandro May 7](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214595287457513): follow-up task [1214592943696234](https://app.asana.com/1/137249556945/project/1212535659026889/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](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214741014878191): "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](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214409112744728). **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](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214627423981810). [Alessandro May 10](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214683268207876): comes for free once task [1213967368170629](https://app.asana.com/1/137249556945/project/1142021229838617/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](https://github.com/duckduckgo/apple-browsers/pull/4855) **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](https://github.com/duckduckgo/apple-browsers/pull/4855/files#r3238549269) **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](https://github.com/duckduckgo/apple-browsers/pull/4855) **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](https://github.com/duckduckgo/apple-browsers/pull/4855) **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](https://github.com/duckduckgo/apple-browsers/pull/4544), Asana task [1214682678629767](https://app.asana.com/1/137249556945/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](https://github.com/duckduckgo/apple-browsers/pull/4544#discussion_r3234645543), `TabViewController.swift:4214`. Also reported as SR GJ6b/ by [Gary Apr 29](https://app.asana.com/1/137249556945/task/1214147157456478/comment/1214409112744728) — 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](https://github.com/duckduckgo/apple-browsers/pull/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](https://app.asana.com/1/137249556945/task/1214147157456478) says *"Ask AI"* (no dot). [Alessandro's #4855 review comment](https://github.com/duckduckgo/apple-browsers/pull/4855) 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](https://app.asana.com/1/137249556945/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](https://app.asana.com/1/137249556945/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](https://app.asana.com/1/137249556945/task/1214147157456478/1214600831548835). 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](https://app.asana.com/1/137249556945/project/1214157224317277/task/1214288645859692) and [O-N Live Onboarding Experiment Details](https://app.asana.com/1/137249556945/task/1214601039604921). **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](https://app.asana.com/1/137249556945/task/1214601039604921))** 🔴 TBD. Provide Pete with final experiment name once rename is decided. Also fill into [O-J <> O-N Coordination](https://app.asana.com/1/137249556945/project/1214157224317277/task/1214288645859692). --- ## Experiment Config (current, local override only) `privacy-configuration/overrides/ios-override.json` under `aiChat` subfeatures: ```json "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`