2026-05-14 media-toolbox-kraken: план обновлён — unresolved 24→8, решения по документалкам, баги 1-6

This commit is contained in:
Alexey Martemyanov
2026-05-14 17:40:48 +06:00
parent 491ead0f8e
commit 0a6fe19857
3 changed files with 276 additions and 110 deletions
@@ -1,83 +1,229 @@
# iOS: Chat-Path Onboarding — Tracker Blocking Demo
> **Asana:** [Ship Review task](https://app.asana.com/1/137249556945/task/1214147157456478)
> **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 | Status |
| PR | Branch | Base | Review status |
|---|---|---|---|
| [#4544](https://github.com/duckduckgo/apple-browsers/pull/4544) | `demo-tracker-blocking-onboarding` | `main` | Open — review required (Alessandro + Rachel left comments) |
| [#4591](https://github.com/duckduckgo/apple-browsers/pull/4591) | `demo-tracker-blocking-onboarding-ui-polish` | #4544 | Approved |
| [#4664](https://github.com/duckduckgo/apple-browsers/pull/4664) | `demo-tracker-blocking-onboarding-chat-path-dialog-polish` | #4591 | Approved |
| [#4855](https://github.com/duckduckgo/apple-browsers/pull/4855) | `demo-tracker-blocking-onboarding-sr-feedback` | #4664 | Open — no review yet. Has uncommitted local changes (see below). |
| [#4668](https://github.com/duckduckgo/apple-browsers/pull/4668) | `demo-tracker-blocking-onboarding-uti-flow` | ? | Open — no review yet. Has open Bugbot + reviewer comments (see below). |
---
## Uncommitted Local Changes — PR #4855 branch (`sr-feedback`)
All of the following are in the working tree but **NOT committed or pushed**. They must be committed before #4855 can be reviewed or merged.
### 1. `NewTabPageViewController.swift` — setBarsHidden + delegate pattern + loading guard
**What changed:**
- Replaced `(parent as? MainViewController)?.setChatPathVisitSiteControlsLocked(true/false)` + `chromeDelegate?.setNavigationBarHidden(true/false)` with a single `chromeDelegate?.setBarsHidden(true/false, animated: false, customAnimationDuration: nil)` call.
- Removed all `(parent as? MainViewController)?` casts from `NewTabPageViewController`.
- Loading guard (`guard ... isLoading != true else { return }`) was a standalone guard that would abort dialog presentation entirely, leaving an orphaned `hostingController` reference. Folded into the outer `if` condition using the new delegate method instead.
- `dismiss()` now calls `setBarsHidden(false)` when `didHideBarsForChatPathVisitSiteDialog` is true.
**Why `setChatPathVisitSiteControlsLocked` calls were removed:**
Alessandro's review comment on [#4855 line 280](https://github.com/duckduckgo/apple-browsers/pull/4855) asked to use `NewTabPageControllerDelegate` instead of casting `(parent as? MainViewController)`. A separate review comment on [#4855 line 477](https://github.com/duckduckgo/apple-browsers/pull/4855) noted Costas's preference to hide both address bar and toolbar via `setBarsHidden(false)`. Using `setBarsHidden` via `chromeDelegate` removes the need for the cast entirely, making `setChatPathVisitSiteControlsLocked` unreachable. The method definition still exists in `MainViewController+DuckAIExperiment.swift:166` as dead code — needs to be deleted.
**Source:** Alessandro review comments on PR #4855 ([line 280](https://github.com/duckduckgo/apple-browsers/pull/4855), [line 473](https://github.com/duckduckgo/apple-browsers/pull/4855), [line 477](https://github.com/duckduckgo/apple-browsers/pull/4855)) + Costas's preference cited therein + Bugbot #4855 "Guard early return aborts entire dialog" + Bugbot #4855 "Navigation bar not restored when NTP dismissed".
**Current state:** ✏️ Uncommitted local change on `sr-feedback` branch.
### 2. `NewTabPageControllerDelegate.swift` + `MainViewController.swift` — new delegate method
**What changed:** Added `newTabPageControllerCurrentTabIsLoading(_ controller: NewTabPageViewController) -> Bool` to the `NewTabPageControllerDelegate` protocol. Implemented in `MainViewController.swift` to return `currentTab?.isLoading == true`.
**Source:** Alessandro review comment on [#4855 line 473](https://github.com/duckduckgo/apple-browsers/pull/4855): "should we add `(parent as? MainViewController)?.currentTab?.isLoading != true` as condition of the if instead of having the guard..." Also addresses Bugbot #4855 "Guard early return aborts entire dialog presentation" and removes the `(parent as?)` cast as requested on [#4855 line 280](https://github.com/duckduckgo/apple-browsers/pull/4855).
**Current state:** ✏️ Uncommitted local change on `sr-feedback` branch.
### 3. `OnboardingIntroViewModel.swift` — returning-user guard + remove hardcoded cohort override
**What changed:**
- Uncommented `guard case .introDialog(isReturningUser: false) = introSteps.first` in `insertExperimentStepIfNeeded()`. This ensures sync-restore / returning users are excluded from the Duck.ai query experiment step.
- Removed hardcoded `return .treatmentB` override from `resolveDuckAIQueryExperimentCohortID()` (was left in for local testing).
**Source (returning-user guard):** [Alessandro May 7 review on PR #4544](https://github.com/duckduckgo/apple-browsers/pull/4544), Asana task [1214682678629767](https://app.asana.com/1/137249556945/task/1214682678629767), Bugbot on #4855 "Returning-user guard commented out despite PR intent". The guard was temporarily commented out during development; it must be active before shipping.
**Source (hardcoded override removal):** Bugbot on #4855 "Hardcoded debug return bypasses feature flag entirely". This was a local testing override (`return .treatmentB`) that must not ship.
**Current state:** ✏️ Uncommitted local change on `sr-feedback` branch.
### 4. `UserText.swift` + all 26 lproj files — `searchAndDuckAIOption` string reverted + "Ask AI" toggle label
**What changed:**
- `searchAndDuckAIOption` reverted to `"Toggle between\nSearch and Duck.ai"` (was changed to "Toggle between\nSearch and Ask AI" in a prior commit).
- DuckAIQuery screen toggle label changed from "Duck.ai" to "Ask AI" in `OnboardingView+DuckAIExperimentSearchContent.swift`.
**Source (revert):** Alessandro review comment on [#4855 UserText.swift:2375](https://github.com/duckduckgo/apple-browsers/pull/4855): *"the rename of 'Duck.ai' to 'Ask AI' is for the toggle we show in the Duck Ai Query screen. The copy for this screen should be reverted."* — meaning the Search Experience screen copy stays as "Duck.ai", only the DuckAI query toggle label becomes "Ask AI".
**Source (Ask AI label):** [GJ8 ship review](https://app.asana.com/1/137249556945/task/1214147157456478): *"change 'Duck.ai' to 'Ask AI' in the toggle screen in onboarding only."*
⚠️ **Unresolved copy question:** Alessandro's comment says "Ask.ai" (with dot). Gary's GJ8 says "Ask AI" (no dot). Needs explicit confirmation from product/design before shipping.
**Current state:** ✏️ Uncommitted local change on `sr-feedback` branch.
### 5. Dead code: `setChatPathVisitSiteControlsLocked` in `MainViewController+DuckAIExperiment.swift:166`
**What:** The method has no callers now that `NewTabPageViewController` uses `setBarsHidden` via the chrome delegate instead. Should be deleted.
**Source:** Removal of callers in item #1 above.
**Current state:** ✏️ Still in code, needs deletion. Uncommitted.
---
## Open GitHub Comment Threads — PR #4544
These Bugbot threads were on PR #4544 and are **not resolved on GitHub**. Status of each:
### [Bugbot] Hardcoded return forces all users into treatment group (OnboardingIntroViewModel.swift)
**Issue:** `return .treatmentA` hardcoded override in `resolveDuckAIQueryExperimentCohortID()`.
**Code state:** The `.treatmentA` override was removed on the `#4544` branch (replaced with `.treatmentB` for testing, then also removed — see uncommitted change #3 above). The `#4544` branch tip no longer has `.treatmentA` hardcoded.
**Thread state:** 🔴 **Not resolved on GitHub.** Bugbot comment was on an old commit — outdated. → Resolve thread.
### [Bugbot] Subscription promo override always returns true (OnboardingSubscriptionPromotionHelper.swift)
**Issue:** `shouldDisplay` was hardcoded to `return true` for testing.
**Code state:** Reverted in commit [`f8d5ba2`](https://github.com/duckduckgo/apple-browsers/commit/f8d5ba2) on #4544 branch.
**Thread state:** 🔴 **Not resolved on GitHub.** Fix is in the branch. → Resolve thread.
### [Bugbot] Chat-path subscription redirect commented out, variable unused (RebrandedNewTabDaxDialogFactory.swift)
**Issue:** `isChatPathSubscriptionPromo` was commented out, variable unused.
**Code state:** Dead code removed in commit [`e682475`](https://github.com/duckduckgo/apple-browsers/commit/e682475) on #4544 branch.
**Thread state:** 🔴 **Not resolved on GitHub.** Fix is in the branch. → Resolve thread.
### [Bugbot] Missing pixel definitions (PixelEvent.swift:2120)
**Issue:** `onboardingChatPathTryVisitSiteUnique` and `onboardingChatPathTrackersBlockedUnique` missing from `PixelEvent`.
**Code state:** Both defined in commit [`ec42fd8`](https://github.com/duckduckgo/apple-browsers/commit/ec42fd8) on #4544 branch (lines 268269, 21192120 of PixelEvent.swift).
**Thread state:** 🔴 **Not resolved on GitHub.** Fix is in the branch. → Resolve thread.
### [Bugbot] Chat-path completion dialog shown twice (MainViewController.swift:4711)
**Issue:** Two competing paths (`tabDidRequestNewTab` dispatch + direct call) could both invoke `presentChatPathOnboardingCompletionIfNeeded()`.
**Code state:** `presentChatPathOnboardingCompletionIfNeeded()` is guarded by `chatPathPhase == .trackerToEOJ`. Whether the double-call race was fully resolved needs verification.
**Thread state:** 🔴 **Not resolved on GitHub.** → Verify code, then resolve thread.
### [Bugbot r3234645543] Chat-path EOJ not shown after tapping tracker dialog CTA (TabViewController.swift:4214)
**Issue:** `didTapDismissContextualOnboardingAction` doesn't call `tabDidRequestNewTab` for chat path, so tapping "Got it" on tracker dialog doesn't open a new tab for the EOJ.
**Code state:** 🔴 **NOT fixed on the #4544 branch.** The fix (`if chatPathPhase == .trackerToEOJ { tabDidRequestNewTab }` in `didTapDismissContextualOnboardingAction`) exists only on the `sr-feedback` branch (commit `6c3774e4f8`). It will be present when the full PR stack merges, but it is not in #4544 itself.
**Thread state:** 🔴 **Not resolved on GitHub.** Fix is not in this PR — it's in #4855. Note this in the thread or backport the fix to #4544.
---
## Open GitHub Comment Threads — PR #4664
### [Alessandro + Bugbot] Title changes for all paths, not just chat path (RebrandedNewTabDaxDialogFactory.swift)
**Issue:** `createSubsequentDialog` title was changed for all flows, not gated on `isChatPath`.
**Code state:** Fixed — `isChatPath` check added (`daxDialogsFlowCoordinator.chatPathPhase == .visitSite`). The rebranded factory now uses the chat-path title conditionally.
**Thread state:** Likely resolved by the fix but confirm on GitHub. → Check and resolve thread.
### [Bugbot] Dismiss button appears on standard path too (RebrandedContextualOnboardingDialogs+SubscriptionPromo.swift:101)
**Issue:** Dismiss button added for chat path appeared on standard path too.
**Code state:** Needs verification on #4664 branch.
**Thread state:** 🔴 **Not confirmed resolved.** → Verify code.
---
## Open GitHub Comment Threads — PR #4668
### [aataraxiaa] Commented-out `Config(...)` line in FeatureFlag.swift:635
**Issue:** `// Config(source: .remoteReleasable(.subfeature(AIChatSubfeature.unifiedToggleInput)))` — a dead commented-out duplicate still in code.
**Source:** [aataraxiaa review comment](https://github.com/duckduckgo/apple-browsers/pull/4668): *"Obviously you just changed this for testing, but just a reminder to change it back before we merge."*
**Code state:** 🔴 **Still in code** on `#4668` branch. The `defaultValue: .enabled` override + commented-out original are both present.
**Thread state:** 🔴 **Not resolved.** → Remove the commented-out line and the `defaultValue: .enabled` before merging.
### [Bugbot] Hardcoded debug return bypasses cohort resolution (OnboardingIntroViewModel.swift:473)
**Issue:** Same `return .treatmentB` override as above.
**Code state:** Removed in the uncommitted local change on `sr-feedback` branch. Will be present once committed and the stack is built. Not yet fixed on #4668 branch itself.
**Thread state:** 🔴 **Not resolved.** → Will be resolved when sr-feedback is merged down or the fix is backported.
### [Bugbot] Protocol method `embedInUnifiedInputEditingAreaIfActive` never called (MainViewController.swift + BrowserChromeManager.swift)
**Issue:** Method added to `BrowserChromeDelegate` protocol and implemented in `BrowserChromeManager`, but never called anywhere.
**Code state:** On the `sr-feedback` branch, commit `82bb0f71e3` removed the `embedDialogInEditingState` dead code path. The method may now be truly unreachable. Needs verification on #4668 branch.
**Thread state:** 🔴 **Not resolved.** → Verify, then either add a caller or delete the method and its implementations.
---
## Open GitHub Comment Threads — PR #4855
All four Alessandro review comments and all Bugbot comments are addressed by the **uncommitted local changes** listed above. The GitHub threads are **not yet resolved** because the changes haven't been committed/pushed.
| Comment | Fix location | Thread resolved? |
|---|---|---|
| [#4544](https://github.com/duckduckgo/apple-browsers/pull/4544) | `demo-tracker-blocking-onboarding` | Open — review required |
| [#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 |
| [#4668](https://github.com/duckduckgo/apple-browsers/pull/4668) | `demo-tracker-blocking-onboarding-uti-flow` | Open — no review yet |
| [#4855](https://github.com/duckduckgo/apple-browsers/pull/4855) | `demo-tracker-blocking-onboarding-sr-feedback` | Open — no review yet |
| Use `NewTabPageControllerDelegate` instead of `(parent as?)` cast (line 280) | Uncommitted — `NewTabPageControllerDelegate.swift` | 🔴 No |
| Hide both bars via `setBarsHidden` per Costas (line 477) | Uncommitted — `NewTabPageViewController.swift` | 🔴 No |
| Fold loading check into `if` condition (line 473) | Uncommitted — `NewTabPageViewController.swift` | 🔴 No |
| Revert `searchAndDuckAIOption` copy (UserText.swift:2375) | Uncommitted — `UserText.swift` + lproj files | 🔴 No |
| Bugbot: Navigation bar not restored when NTP dismissed (line 281) | Uncommitted — `NewTabPageViewController.swift` `dismiss()` | 🔴 No |
| Bugbot: Guard early return aborts dialog presentation (line 473) | Uncommitted — guard folded into `if` | 🔴 No |
| Bugbot: Hardcoded debug return bypasses feature flag (OnboardingIntroViewModel:473) | Uncommitted — hardcoded return removed | 🔴 No |
| Bugbot: Returning-user guard commented out (OnboardingIntroViewModel:451) | Uncommitted — guard uncommented | 🔴 No |
**Commit and push all local changes first. Then resolve these threads.**
---
## Required Changes
## Pre-Ship Blockers
### PR #4544
- [x] Danger CI: no `@UserDefaultsWrapper` remains
- [x] Mock refactor (alessandroboron): `MockDaxDialogsSettings.chatPathPhase` plain stored property
- [x] **Sync restore exclusion** — [Alessandro May 7](https://app.asana.com/1/137249556945/task/1214147157456478): *"if the user selects 'Restore My Stuff' we disable Dax dialogs — we should not enrol those users in the experiment"*. Fixed: uncommented `guard case .introDialog(isReturningUser: false) = introSteps.first` in `insertExperimentStepIfNeeded()`
- [x] `return .treatmentA` override removed from `resolveDuckAIQueryExperimentCohortID()`
- [x] Pixels: `onboardingChatPathTryVisitSiteUnique` / `onboardingChatPathTrackersBlockedUnique` defined, no overlap with PR #4687
### 1. Commit and push local changes on `sr-feedback`
### PR #4664
- [x] Title scope fix (alessandroboron): `createSubsequentDialog` uses correct title per chat-path vs standard path
All changes in the "Uncommitted Local Changes" section above must be committed to `alex/demo-tracker-blocking-onboarding-sr-feedback` and pushed before PR #4855 can be reviewed. Then the eight GitHub threads above can be resolved.
### PR #4668
- [x] Dialog position, dismiss, UTI bar, completion path resolved
- [x] `.unifiedToggleInput` feature flag has no hardcoded `defaultValue: .enabled`
**Current state:** 🔴 Not done.
### PR #4855
- [x] `setBarsHidden(true/false, animated: false, customAnimationDuration: nil)` replaces `setNavigationBarHidden` + `setChatPathVisitSiteControlsLocked` in show/dismiss/dismissHostingController
- [x] `newTabPageControllerCurrentTabIsLoading` added to `NewTabPageControllerDelegate`; all `(parent as? MainViewController)?` casts gone from `NewTabPageViewController`
- [x] Loading guard folded into outer `if` via delegate method
- [x] `searchAndDuckAIOption` reverted to `"Toggle between\nSearch and Duck.ai"` in `UserText.swift` + all 26 lproj files
- [x] **DuckAIQuery toggle copy** — [Gary May 6](https://app.asana.com/1/137249556945/task/1214147157456478): *"change 'Duck.ai' to 'Ask AI' in the toggle screen in onboarding only, not the standard toggle"*. Fixed: `DuckAIQueryExperiment.toggleAILabel = "Ask AI"` (uses `NotLocalizedString`)
### 2. Tell UTI team the experiment name
**Source:** [Pete Apr 30 comment](https://app.asana.com/1/137249556945/task/1214147157456478/1214600831548835): Pete's team will exclude users enrolled in this experiment from the UTI feature rollout, to avoid breaking their onboarding. They need the final experiment name.
**Current state:** 🔴 Blocked — experiment name not final (see #4 below). Once known: fill 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).
### 3. Privacy config: add US/EN targeting to override
**Source:** [Task 1214682678629768](https://app.asana.com/1/137249556945/task/1214682678629768): experiment must be limited to US English users. Without this, all locales worldwide would be enrolled.
**What:** In `privacy-configuration/overrides/ios-override.json`, add `"targets": [{ "localeLanguage": "en", "localeCountry": "US" }]` to the experiment entry.
**Current state:** 🔴 Local edit exists on wrong branch (`alex/remove-autocomplete-tabs`) in the `privacy-configuration` repo, not committed.
### 4. Privacy config: rename the experiment
**Source:** [Task 1214682678629768](https://app.asana.com/1/137249556945/task/1214682678629768): some internal users were enrolled under `onboardingDuckAIQueryExperiment` during testing. A new name gives production users a clean, separate enrollment.
**What — three-part change:**
1. **BSK** (`SharedPackages/BrowserServicesKit`): add a new case to `AIChatSubfeature` enum with the new name string.
2. **App** (`iOS/Core/FeatureFlag.swift`): update `FeatureFlag.onboardingDuckAIQueryExperiment` to reference the new subfeature case.
3. **`privacy-configuration` repo** (`overrides/ios-override.json`): add new experiment entry with `targets`, and set the old `onboardingDuckAIQueryExperiment` entry to `"state": "disabled"`.
Do this in the same PR as item #3 (both touch the same file in the same repo).
**Current state:** 🔴 Blocked — new experiment name not decided.
### 5. Fix commented-out feature flag line in PR #4668
**Source:** aataraxiaa review comment on #4668 (see Open Threads above).
**What:** Remove `defaultValue: .enabled` and the `// Config(...)` dead line from `FeatureFlag.swift:635` for `unifiedToggleInput`.
**Current state:** 🔴 Still in code on `#4668` branch. Not fixed.
### 6. Clarify "Ask AI" vs "Ask.ai" copy
**Source:** Conflict between GJ8 ("Ask AI") and Alessandro's review comment ("Ask.ai").
**Current state:** 🔴 Implemented as "Ask AI" (no dot) per GJ8. Awaiting explicit product/design confirmation.
---
## Before Going Live
## Experiment Config (current, local override)
### 1. UTI experiment exclusion
**Source:** [Pete Apr 30, ship review](https://app.asana.com/1/137249556945/task/1214147157456478/1214600831548835): *"to avoid impacting any live onboarding experiments we can exclude those experiment participants when we release. Continue coordination in [O-J <> O-N Coordination](https://app.asana.com/1/137249556945/project/1214157224317277/task/1214288645859692)."*
Task [O-J <> O-N Coordination](https://app.asana.com/1/137249556945/project/1214157224317277/task/1214288645859692) (owned by Pete) lists O-N experiments the UTI team needs to be aware of for exclusion at UTI rollout. Already lists this iOS experiment under "iOS - O-N will handle".
- [ ] Confirm experiment is listed and named correctly in [O-J <> O-N Coordination](https://app.asana.com/1/137249556945/project/1214157224317277/task/1214288645859692) before going live.
### 2. Privacy config targeting
- [x] Added `"targets": [{ "localeLanguage": "en", "localeCountry": "US" }]` to `onboardingDuckAIQueryExperiment` in `privacy-configuration/overrides/ios-override.json`
### 3. Experiment rename
No explicit Asana comment or PR review requested this — no source found.
- [ ] **Decide:** rename `onboardingDuckAIQueryExperiment` before production rollout? If yes: requires new `AIChatSubfeature` case in BSK + new privacy config entry + disable old one.
---
## Resolved / Dismissed
- **GJ6c** — No longer required; address bar hidden for visit-site step; Gary + Costas confirmed search after trackers-blocked is acceptable
- **GJ2** — X button removed from chat-path dialogs (PR #4664)
- **GJ3** — Address bar + toolbar hidden for visit-site step (chat-path only)
- **GJ4** — Copy: "Next, try visiting a site!" after AI
- **GJ7** — Returning users excluded from experiment enrollment
- **GJ8** — Onboarding toggle shows "Ask AI"; Search Experience screen keeps "Duck.ai"
- **Fire tabs flash** — Suppressed via `isStillOnboarding()` in `DaxDialogs`
---
## Experiment Config (current)
Entry in `privacy-configuration/overrides/ios-override.json` under `aiChat` subfeatures:
`privacy-configuration/overrides/ios-override.json` under `aiChat` subfeatures:
```json
"onboardingDuckAIQueryExperiment": {
@@ -91,5 +237,5 @@ Entry in `privacy-configuration/overrides/ios-override.json` under `aiChat` subf
}
```
> Feature flag: `FeatureFlag.onboardingDuckAIQueryExperiment`
> Feature flag: `FeatureFlag.onboardingDuckAIQueryExperiment` in `iOS/Core/FeatureFlag.swift`
> Cohort type: `FeatureFlag.DuckAIQueryExperimentCohort`