From e7c8cdd55ea304594331ba9b6827a2e0d84513f5 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Thu, 14 May 2026 16:03:18 +0600 Subject: [PATCH] Failed to authenticate. API Error: 401 Invalid authentication credentials [2026-05-14] vault sync --- work/plans/no-topic-routing.md | 8 +- .../chat-path-onboarding-tracker-blocking.md | 82 +++++++------------ 2 files changed, 33 insertions(+), 57 deletions(-) diff --git a/work/plans/no-topic-routing.md b/work/plans/no-topic-routing.md index 8ac9f007..2810a88e 100644 --- a/work/plans/no-topic-routing.md +++ b/work/plans/no-topic-routing.md @@ -1,7 +1,7 @@ # Plan: (no topic) Thread Routing **Date**: 2026-05-14 -**Status**: In Progress — plugin being implemented +**Status**: In Progress — plugin created, pending test **Priority**: High ## Problem @@ -111,9 +111,9 @@ Dropped for v1 — single Eagle instance in practice. Can add mutex later if nee - [x] Confirmed curl approach works for topic creation - [x] Identified `pre_gateway_dispatch` as correct hook - [x] Confirmed `thread_id == "(no topic)"` detection pattern -- [ ] Create `~/.hermes/plugins/zulip-topic-routing/plugin.yaml` -- [ ] Create `~/.hermes/plugins/zulip-topic-routing/__init__.py` -- [ ] Enable plugin in `~/.hermes/config.yaml` +- [x] Create `~/.hermes/plugins/zulip-topic-routing/plugin.yaml` +- [x] Create `~/.hermes/plugins/zulip-topic-routing/__init__.py` +- [x] Enable plugin in `~/.hermes/config.yaml` - [ ] Test: send `(no topic)` message, verify routing ## Notes diff --git a/work/wiki/apple-browsers/chat-path-onboarding-tracker-blocking.md b/work/wiki/apple-browsers/chat-path-onboarding-tracker-blocking.md index 0622a42f..6d032287 100644 --- a/work/wiki/apple-browsers/chat-path-onboarding-tracker-blocking.md +++ b/work/wiki/apple-browsers/chat-path-onboarding-tracker-blocking.md @@ -10,7 +10,7 @@ A/B experiment adding a tracker-blocking demo step to onboarding for users who p 1. "Try visiting a site!" dialog (address bar + toolbar hidden) 2. Trackers-blocked dialog after a site loads 3. "You've got this!" EOJ completion dialog -4. Subscription upsell ("Oh, before I forget…") +4. Subscription upsell ("Oh, before I forget...") **Cohorts:** Treatment A (address bar top) · Treatment B (address bar bottom) · Control (no change) **Audience:** US only · iPhone only · Users who chose Duck.ai in onboarding, in A or B cohort @@ -29,56 +29,32 @@ A/B experiment adding a tracker-blocking demo step to onboarding for users who p --- -## Required Changes (not yet done) +## Required Changes ### PR #4544 — main chat-path branch -- [x] **Danger CI**: `@UserDefaultsWrapper(key: .daxChatPathVisitSiteSeen)` — key is only defined as an enum case; no `@UserDefaultsWrapper` usage remains. Already resolved. -- [x] **Mock refactor** (alessandroboron): `MockDaxDialogsSettings.chatPathPhase` is already a plain stored property (`var chatPathPhase: DaxDialogs.ChatPathPhase = .none`), directly settable in tests. Already resolved. -- [ ] **Sync restore exclusion**: Exclude users who selected "Restore My Stuff" (sync restore) from experiment enrollment. - - **Why it breaks**: When sync data is restored, the synced `homeScreenMessagesSeen > 0` value from the user's prior device triggers `DaxDialogsOnboardingMigrator.migrateFromOldToNewOnboarding()` to set `isDismissed = true`, which kills all contextual Dax dialogs (including the chat-path visit-site and trackers-blocked steps). - - **Current state**: The returning-user guard in `insertExperimentStepIfNeeded()` (`OnboardingIntroViewModel.swift:451`) is commented out: `// guard case .introDialog(isReturningUser: false) = introSteps.first,`. Sync-restore users have `isReturningUser: true` (via `OnboardingManager.stepsForCurrentFlow()`) so uncommenting this guard is sufficient to exclude them. - - **Action**: In `OnboardingIntroViewModel.insertExperimentStepIfNeeded()`, uncomment the line `// guard case .introDialog(isReturningUser: false) = introSteps.first,`. This excludes all returning users (sync restore + returning installs) from the experiment. -- [ ] **Pixels**: Coordinate with rachelmcr (PR [#4687](https://github.com/duckduckgo/apple-browsers/pull/4687)). - - **Current state**: Two new pixel events `m_onboarding_chat-path_try-visit-site_unique` and `m_onboarding_chat-path_trackers-blocked_unique` were added in `PixelEvent.swift` but have no `.json5` definition in `iOS/PixelDefinitions/`. - - **Preferred approach**: PR #4687 adds a `variant` parameter to existing onboarding pixels. If it merges first, the chat-path steps can use the existing pixels with `variant: "chat-path"` instead of new event names — remove the two new pixel event definitions from `PixelEvent.swift` and use the variant approach. - - **Fallback**: If #4687 doesn't merge in time, add entries for both new pixels to `iOS/PixelDefinitions/pixels/definitions/onboarding.json5`. +- [x] **Danger CI**: No `@UserDefaultsWrapper` usage remains. +- [x] **Mock refactor** (alessandroboron): `MockDaxDialogsSettings.chatPathPhase` is a plain stored property, directly settable in tests. +- [x] **Sync restore exclusion**: Uncommented `guard case .introDialog(isReturningUser: false) = introSteps.first` in `OnboardingIntroViewModel.insertExperimentStepIfNeeded()`. Excludes all returning users (including sync-restore) from experiment enrollment. +- [x] **`return .treatmentA` override**: Removed hardcoded early return from `resolveDuckAIQueryExperimentCohortID()`. Now routes through the feature flagger. +- [x] **Pixels**: `onboardingChatPathTryVisitSiteUnique` and `onboardingChatPathTrackersBlockedUnique` defined in `PixelEvent.swift` with matching `onboarding.json5` entries. No overlap with PR #4687. ### PR #4664 — chat-path dialog polish -- [x] **Title scope** (alessandroboron): Fixed in commit `c15122a1b0` — `RebrandedNewTabDaxDialogFactory.createSubsequentDialog` now correctly uses `isChatPath ? onboardingTryASiteTitle : onboardingTryASiteNTPTitle`. Already resolved. +- [x] **Title scope** (alessandroboron): Fixed in commit `c15122a1b0` — `RebrandedNewTabDaxDialogFactory.createSubsequentDialog` uses the correct title per chat-path vs standard path. -### PR #4668 — UTI (Unified Toggle Input) onboarding flow fixes +### PR #4668 — UTI onboarding flow fixes -Fixes to make the chat-path onboarding dialogs work correctly when the Unified Toggle Input is active: - -- [x] Dialog position: anchor to `coordinator.viewController.view.bottomAnchor` (UTI bar bottom), embed in `unifiedInputContentContainer`. -- [x] Dialog not dismissed on navigation: `viewWillDisappear` removes hosting controller parented to `MainViewController`. -- [x] UTI bar staying active after URL submission: `preserveOmnibarSession` now checks `!tab.isLoading`. -- [x] "You've got this" completion dialog path in UTI mode: `showDuckAIOnboardingCompletionDialogInUTI`. -- [x] UTI bar staying active after completion dismiss: call `dismissUnifiedToggleInputToOmnibar` directly. -- [x] Stale browsing-dialog flags: `setAsChatFirstPath` now resets all browsing-dialog flags to prevent `chatPathPhase` jumping to `.trackerToEOJ` from stale state. -- [ ] **Hardcoded UTI override** (aataraxiaa): `FeatureFlag.swift` `.unifiedToggleInput` case has `defaultValue: .enabled` hardcoded (forces UTI on for testing). The original config is commented out on the next line. - - **Action**: Revert to `Config(source: .remoteReleasable(.subfeature(AIChatSubfeature.unifiedToggleInput)))` — remove `defaultValue: .enabled`. - - **Note**: `OnboardingIntroViewModel.resolveDuckAIQueryExperimentCohortID()` also has `return .treatmentA` hardcoded before the real flag logic. Both must be reverted before merge (the `resolveDuckAIQueryExperimentCohortID` fix is tracked under PR #4544 returning-user guard work above). - -### PR #4591 — ui-polish - -- [x] Approved. Rebase on current base. +- [x] Dialog position, dismiss on navigation, UTI bar, completion path — all resolved. +- [x] **Hardcoded UTI override**: `FeatureFlag.swift` `.unifiedToggleInput` confirmed correct — `Config(source: .remoteReleasable(...))`, no `defaultValue: .enabled`. ### PR #4855 — sr-feedback -- [ ] **Hide toolbar + address bar**: Replace current approach (separate `setNavigationBarHidden(true)` + `setChatPathVisitSiteControlsLocked(true)`) with `chromeDelegate?.setBarsHidden(true)`. - - **Current code**: `NewTabPageViewController.swift:472-478` hides the nav bar and locks toolbar controls separately. - - **Requested approach** (Costas + Alessandro): `chromeDelegate?.setBarsHidden(true/false)` hides both address bar and toolbar in one call, which is simpler and matches how other parts of the app hide chrome. - - **Action**: In `showNextDaxDialogNew` (the visit-site `spec == .subsequent` block): replace `setNavigationBarHidden(true)` + `setChatPathVisitSiteControlsLocked(true)` with `chromeDelegate?.setBarsHidden(true)`. In `dismiss()` and `dismissHostingController`: replace `setNavigationBarHidden(false)` + `setChatPathVisitSiteControlsLocked(false)` with `chromeDelegate?.setBarsHidden(false)`. -- [ ] **Use delegate**: `NewTabPageViewController` casts to `(parent as? MainViewController)?` in several places (e.g. lines 280, 473, 475, 510) to call `setChatPathVisitSiteControlsLocked` and check `currentTab?.isLoading`. - - **Action**: Add `func newTabPageControllerWillShowChatPathVisitSiteDialog()` and `func newTabPageControllerDidDismissChatPathVisitSiteDialog()` (or equivalent) to `NewTabPageControllerDelegate`, implement them in `MainViewController`, and route the locking/bar-hiding calls through the delegate instead of casting. - - **Note**: Once bars are hidden via `setBarsHidden` (item above), the `setChatPathVisitSiteControlsLocked` call may be removed entirely, simplifying the delegate surface. -- [ ] **Loading guard placement**: `NewTabPageViewController.swift:473`: `guard (parent as? MainViewController)?.currentTab?.isLoading != true else { return }` exits early and leaves `didHideBarsForChatPathVisitSiteDialog` unset, resulting in an orphaned lock and a blank NTP if a tab happens to be loading. - - **Action**: Fold the condition into the outer `if` block: `if spec == .subsequent, chatPathPhase == .visitSite, (parent as? MainViewController)?.currentTab?.isLoading != true { ... }` so the whole block (including `didHideBarsForChatPathVisitSiteDialog = true`) is skipped atomically. -- [ ] **Revert Search Experience screen copy**: `UserText.swift:2374` `searchAndDuckAIOption` value is currently `"Toggle between\nSearch and Ask AI"` — needs reverting to `"Toggle between\nSearch and Duck.ai"`. - - **Why**: The "Ask AI" rename (GJ8) applies only to the DuckAIQuery experiment toggle, which now uses the dedicated `DuckAIQueryExperiment.toggleAILabel = "Ask.ai"` string. The Search Experience screen in standard onboarding must keep "Duck.ai". +- [x] **Hide toolbar + address bar**: `NewTabPageViewController` now calls `chromeDelegate?.setBarsHidden(true/false, animated: false, customAnimationDuration: nil)` in the show block, `dismiss()`, and `dismissHostingController`. Replaces previous `setNavigationBarHidden` + `setChatPathVisitSiteControlsLocked` pair. +- [x] **Use delegate**: Added `newTabPageControllerCurrentTabIsLoading(_ controller:) -> Bool` to `NewTabPageControllerDelegate` (default `false`). Implemented in `MainViewController` as `currentTab?.isLoading == true`. All `(parent as? MainViewController)?` casts removed from `NewTabPageViewController`. +- [x] **Loading guard placement**: Folded into the outer `if` condition via the new delegate method. No orphaned lock possible. +- [x] **Revert Search Experience screen copy**: `UserText.swift` `searchAndDuckAIOption` reverted to `"Toggle between\nSearch and Duck.ai"`. All 26 lproj files updated. +- [x] **DuckAIQuery toggle copy**: `DuckAIQueryExperiment.toggleAILabel` = `"Ask AI"`. --- @@ -92,13 +68,13 @@ Fixes to make the chat-path onboarding dialogs work correctly when the Unified T ## Resolved / Dismissed -- **GJ6c** ✅ — No longer required. Address bar hidden for visit-site step (GJ3), so user can't trigger a search at that point. Costas confirmed a search after the trackers-blocked dialog is acceptable. -- **GJ3** ✅ — Hide address bar + toolbar for "Try visiting a site" step (chat-path only). -- **GJ2** ✅ — X button removed from chat-path dialogs (PR #4664). -- **GJ4** ✅ — Copy updated to "Next, try visiting a site!" after AI. -- **GJ8** ✅ — "Duck.ai" → "Ask.ai" in DuckAIQuery toggle (onboarding only, not standard toggle). `DuckAIQueryExperiment.toggleAILabel = "Ask.ai"`. -- **"Try fire tabs" flash** ✅ — Suppressed via `isStillOnboarding()` in `DaxDialogs` for chat-path users. -- **GJ7 (search-only path breakage)** ✅ — Returning users guarded from experiment enrollment. +- **GJ6c** — No longer required. +- **GJ3** — Hide address bar + toolbar for "Try visiting a site" step (chat-path only). +- **GJ2** — X button removed from chat-path dialogs (PR #4664). +- **GJ4** — Copy updated to "Next, try visiting a site!" after AI. +- **GJ8** — DuckAIQuery experiment toggle shows "Ask AI" (onboarding only; Search Experience screen keeps "Duck.ai"). +- **"Try fire tabs" flash** — Suppressed via `isStillOnboarding()` in `DaxDialogs` for chat-path users. +- **GJ7** — Returning users guarded from experiment enrollment. --- @@ -108,10 +84,10 @@ Fixes to make the chat-path onboarding dialogs work correctly when the Unified T |---|---| | `iOS/DuckDuckGo/DaxDialogs.swift` | Core onboarding dialog logic, chat-path phase state machine | | `iOS/DuckDuckGo/OnboardingFlow/LinearOnboarding/OnboardingIntroViewModel.swift` | Experiment enrollment, step injection | -| `iOS/DuckDuckGo/NewTabPageViewController.swift` | Visit-site dialog presentation, toolbar hiding | +| `iOS/DuckDuckGo/NewTabPageViewController.swift` | Visit-site dialog presentation, bar hiding | | `iOS/DuckDuckGo/MainViewController+DuckAIExperiment.swift` | Chat-path completion, EOJ dialog trigger | | `iOS/Core/FeatureFlag.swift` | `onboardingDuckAIQueryExperiment` feature flag + cohorts | -| `iOS/DuckDuckGo/UserText.swift` | Onboarding copy strings (DuckAIQueryExperiment, SearchExperience) | +| `iOS/DuckDuckGo/UserText.swift` | Onboarding copy strings | | `iOS/DuckDuckGo/OnboardingFlow/LinearOnboarding/OnboardingView+DuckAIExperimentSearchContent.swift` | DuckAIQuery toggle + experiment UI | | `iOS/DuckDuckGo/OnboardingFlow/ContextualOnboarding/Rebranding/RebrandedNewTabDaxDialogFactory.swift` | Chat-path dialog creation | @@ -119,10 +95,10 @@ Fixes to make the chat-path onboarding dialogs work correctly when the Unified T ## Copy Changes Summary -| Location | Old | New | Status | -|---|---|---|---| -| `DuckAIQueryExperiment.toggleAILabel` (onboarding toggle) | `Duck.ai` | `Ask.ai` | ✅ Done | -| `searchAndDuckAIOption` (Search Experience screen) | `Toggle between\nSearch and Duck.ai` | *(should remain Duck.ai)* | ❌ Needs revert | +| Location | Value | Status | +|---|---|---| +| `DuckAIQueryExperiment.toggleAILabel` (onboarding experiment toggle) | `Ask AI` | Done | +| `searchAndDuckAIOption` (Search Experience screen) | `Toggle between\nSearch and Duck.ai` | Done | ---