Add tab group suggestions feature - #52
Conversation
Introduces a suggestion engine that recommends grouping a user's currently open tabs based on independent signals: shared domain, recency, visit frequency, and shared title keywords. Signals are scored with tunable weights, ranked, deduplicated and capped into a small set of suggestions. Suggestions are surfaced as a dismissible card on the home screen and as a snackbar action from the tabs tray. Dismissed suggestions and an opt-out toggle persist across restarts via a DataStore-backed preferences store. Glean events record when a suggestion is shown, accepted, or dismissed. Adds: - Domain/signal/scoring/ranking logic under org.mozilla.fenix.components.tabgroupsuggestions, with a lib-state Store/Action/Reducer/Middleware for UI-facing state. - Compose UI for the home screen card under org.mozilla.fenix.compose.tabgroupsuggestions. - Home screen and tabs tray integration points, plus a settings screen to toggle the feature and reset dismissed suggestions. - A tabGroupSuggestionsFeature flag, new Glean metrics, and new strings following existing conventions. - Unit tests for the engine, scorer, ranker, signals, repository, preferences, diffing and reducer.
|
| Severity | File | Description |
|---|---|---|
| 🔵 Low | …/components/Components.kt |
Unmanaged MainScope in singleton store middleware can leak w |
📖 Walkthrough
This change introduces a feature-flagged “Tab Group Suggestions” system end-to-end: telemetry (shown/accepted/dismissed and section visibility), a scoring/ranking engine driven by multiple signals, persistence of dismissed/shown state via DataStore, and a Redux-style store with middleware to coordinate refresh/accept/dismiss/opt-out flows. It wires suggestions into Home and Tabs Tray UI (Compose card + snackbar) and adds a dedicated settings screen. Extensive unit tests cover signals, engine, diffing, persistence, and state transitions.
🔀 Sequence
sequenceDiagram
participant BrowserStore
participant TabGroupSuggestionFeature
participant TabGroupSuggestionEngine
participant TabGroupSuggestionStore
participant Repository
participant UI as Home/TabsTray UI
participant Telemetry as Glean Telemetry
BrowserStore->>TabGroupSuggestionFeature: Open tabs change event
TabGroupSuggestionFeature->>TabGroupSuggestionEngine: Debounced refresh request
TabGroupSuggestionEngine->>TabGroupSuggestionEngine: Score + rank via signals
TabGroupSuggestionEngine->>TabGroupSuggestionStore: Dispatch Update/Refresh results
TabGroupSuggestionStore->>Repository: Persist dismissed/shown/opt-out changes
TabGroupSuggestionStore->>Telemetry: Record shown/accepted/dismissed + visibility
TabGroupSuggestionStore->>UI: Expose state (loading/empty/content/top suggestion)
UI->>TabGroupSuggestionStore: User accepts/dismisses/opts-out/reset dismissed
📂 File Changes
📊 Changes by Category (6 categories)
🔧 Tab Group Suggestions engine, scoring, and state management
Core implementation of tab group suggestions: feature wiring/components, ranking/scoring signals, suggestion model + diffing behavior, persistence of dismissed/last-shown data, and store (actions/reducer/middleware/state) to manage refresh/accept/dismiss/opt-out flows.
| Files | Summary |
|---|---|
app/src/main/java/org/mozilla/fenix/components/Components.ktapp/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionFeature.ktapp/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionEngine.kt |
Adds tab group suggestion feature components and wiring (preferences, repository, engine, store, middleware) and refreshes (debounced) on open tab changes to push updates into the store. |
app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/SuggestionSignal.ktapp/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/SuggestionScorer.ktapp/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/SuggestionRanker.ktapp/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/signals/DomainSimilaritySignal.ktapp/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/signals/FrequencySignal.ktapp/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/signals/RecencySignal.ktapp/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/signals/TitleKeywordSignal.kt |
Adds the tab group suggestion scoring/ranking pipeline and signals (domain similarity, frequency, recency, title keywords). |
app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestion.ktapp/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionDiffing.kt |
Adds TabGroupSuggestion model with stable ID derived from sorted tab IDs and diffing logic to drop dismissed suggestions while preserving timestamps when unchanged. |
app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionPreferences.ktapp/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionRepository.ktapp/src/main/java/org/mozilla/fenix/utils/Settings.kt |
Adds DataStore-backed persistence and preferences wiring for tab group suggestions (dismissed IDs, last shown timestamps, and tabGroupSuggestionsEnabled enable/disable). |
app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/store/TabGroupSuggestionAction.ktapp/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/store/TabGroupSuggestionMiddleware.ktapp/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/store/TabGroupSuggestionReducer.ktapp/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/store/TabGroupSuggestionState.ktapp/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/store/TabGroupSuggestionStore.kt |
Adds tab group suggestions store layer (state/status, actions, reducer, middleware, store) to manage updates/accept/dismiss/opt-out/refresh, emit telemetry, and persist state changes. |
🎨 User-facing surfaces (Home, Tabs Tray) and Compose presentation
Adds Compose UI components for rendering suggestions and integrates the feature into Home and Tabs Tray experiences (including preview/top suggestion selection, snackbar bindings, and acceptance/dismiss interactions).
| Files | Summary |
|---|---|
app/src/main/java/org/mozilla/fenix/compose/tabgroupsuggestions/TabGroupSuggestionCard.ktapp/src/main/java/org/mozilla/fenix/compose/tabgroupsuggestions/TabGroupSuggestionDismissButton.ktapp/src/main/java/org/mozilla/fenix/compose/tabgroupsuggestions/TabGroupSuggestionEmptyState.ktapp/src/main/java/org/mozilla/fenix/compose/tabgroupsuggestions/TabGroupSuggestionRow.kt |
Adds Compose UI for tab group suggestions including card (loading/empty/content), row with favicon handling, dismiss button, empty state, and previews. |
app/src/main/java/org/mozilla/fenix/home/HomeFragment.ktapp/src/main/java/org/mozilla/fenix/home/tabgroupsuggestions/TabGroupSuggestionsSectionUseCase.ktapp/src/main/java/org/mozilla/fenix/home/tabgroupsuggestions/TabGroupSuggestionsViewHolder.kt |
Wires tab group suggestions into Home UI: bridges BrowserStore to TabGroupSuggestionStore, computes previews/top suggestion, and integrates Compose card via a store-backed view holder/interactor plus navigation helpers. |
app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.ktapp/src/main/java/org/mozilla/fenix/tabstray/tabgroupsuggestions/TabGroupSuggestionsTrayBinding.kt |
Adds feature-flagged tab group suggestions UI wiring in tabs tray, surfacing the top suggestion via snackbar bindings and enabling acceptance via snackbar action callbacks. |
🎨 Settings controls and navigation for Tab Group Suggestions
Introduces a dedicated settings screen and navigation/strings/keys to enable/disable tab group suggestions and reset dismissed suggestions, with gating applied where appropriate.
| Files | Summary |
|---|---|
app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.ktapp/src/main/java/org/mozilla/fenix/settings/TabGroupSuggestionSettingsFragment.ktapp/src/main/res/navigation/nav_graph.xmlapp/src/main/res/values/preference_keys.xmlapp/src/main/res/values/strings.xmlapp/src/main/res/xml/preferences.xmlapp/src/main/res/xml/tab_group_suggestions_preferences.xml |
Adds tab group suggestions settings UI and navigation (new settings fragment/screen, enable switch + reset dismissed, strings/keys), and gates settings visibility behind a feature flag. |
📊 Telemetry for Tab Group Suggestions
Defines and wires Glean telemetry for tab group suggestion events (shown/accepted/dismissed) and section visibility tracking.
| Files | Summary |
|---|---|
app/metrics.yamlapp/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionTelemetry.kt |
Adds new tab_group_suggestions telemetry events (shown, accepted, dismissed) plus section visibility tracking, and wires logging via a Glean telemetry wrapper. |
🔧 Feature flag gating by release channel
Adds a feature flag to gate tab group suggestions availability on nightly/beta channels.
| Files | Summary |
|---|---|
app/src/main/java/org/mozilla/fenix/FeatureFlags.kt |
Adds tab group suggestions feature flag gated on nightly/beta channels. |
🧪 Unit test coverage for engine, store, persistence, and ranking
Adds comprehensive unit tests covering the scoring/ranking pipeline, engine behavior, diffing/timestamp reuse logic, DataStore-backed preferences/repository, and reducer state transitions (refresh/update/accept/dismiss/opt-out).
| Files | Summary |
|---|---|
app/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/SuggestionRankerTest.ktapp/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/SuggestionScorerTest.ktapp/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionEngineTest.ktapp/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/signals/DomainSimilaritySignalTest.ktapp/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/signals/FrequencySignalTest.ktapp/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/signals/RecencySignalTest.ktapp/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/signals/TitleKeywordSignalTest.kt |
Adds extensive unit tests for scoring, ranking, engine behavior, and individual signals (including edge cases). |
app/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionDiffingTest.kt |
Adds unit tests for tab group suggestion diffing and timestamp reuse logic. |
app/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionPreferencesTest.ktapp/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionRepositoryTest.kt |
Adds unit tests for tab group suggestion preferences/serializer and repository interactions. |
app/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/store/TabGroupSuggestionReducerTest.kt |
Adds comprehensive unit tests for the TabGroupSuggestion reducer, covering refresh, updates, accept/dismiss, opt-out, and state transitions. |
🛡️ Security Analysis Report✅ No Security Issues FoundThis PR passed all automated security checks, and no additional model-identified security findings were observed. Powered by Devzy Security Scanner - Detecting 90+ security patterns across OWASP Top 10, cloud secrets, and infrastructure security. security-summary |
There was a problem hiding this comment.
Review complete
DevzyAi finished this review for this commit. Feedback is in the inline review comments on this diff.
Files selected (50)
- app/metrics.yaml (1)
- app/src/main/java/org/mozilla/fenix/FeatureFlags.kt (1)
- app/src/main/java/org/mozilla/fenix/components/Components.kt (3)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/SuggestionRanker.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/SuggestionScorer.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/SuggestionSignal.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestion.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionDiffing.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionEngine.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionFeature.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionPreferences.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionRepository.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionTelemetry.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/signals/DomainSimilaritySignal.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/signals/FrequencySignal.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/signals/RecencySignal.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/signals/TitleKeywordSignal.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/store/TabGroupSuggestionAction.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/store/TabGroupSuggestionMiddleware.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/store/TabGroupSuggestionReducer.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/store/TabGroupSuggestionState.kt (1)
- app/src/main/java/org/mozilla/fenix/components/tabgroupsuggestions/store/TabGroupSuggestionStore.kt (1)
- app/src/main/java/org/mozilla/fenix/compose/tabgroupsuggestions/TabGroupSuggestionCard.kt (1)
- app/src/main/java/org/mozilla/fenix/compose/tabgroupsuggestions/TabGroupSuggestionDismissButton.kt (1)
- app/src/main/java/org/mozilla/fenix/compose/tabgroupsuggestions/TabGroupSuggestionEmptyState.kt (1)
- app/src/main/java/org/mozilla/fenix/compose/tabgroupsuggestions/TabGroupSuggestionRow.kt (1)
- app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt (6)
- app/src/main/java/org/mozilla/fenix/home/tabgroupsuggestions/TabGroupSuggestionsSectionUseCase.kt (1)
- app/src/main/java/org/mozilla/fenix/home/tabgroupsuggestions/TabGroupSuggestionsViewHolder.kt (1)
- app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt (2)
- app/src/main/java/org/mozilla/fenix/settings/TabGroupSuggestionSettingsFragment.kt (1)
- app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt (5)
- app/src/main/java/org/mozilla/fenix/tabstray/tabgroupsuggestions/TabGroupSuggestionsTrayBinding.kt (1)
- app/src/main/java/org/mozilla/fenix/utils/Settings.kt (1)
- app/src/main/res/navigation/nav_graph.xml (2)
- app/src/main/res/values/preference_keys.xml (1)
- app/src/main/res/values/strings.xml (1)
- app/src/main/res/xml/preferences.xml (1)
- app/src/main/res/xml/tab_group_suggestions_preferences.xml (1)
- app/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/SuggestionRankerTest.kt (1)
- app/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/SuggestionScorerTest.kt (1)
- app/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionDiffingTest.kt (1)
- app/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionEngineTest.kt (1)
- app/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionPreferencesTest.kt (1)
- app/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/TabGroupSuggestionRepositoryTest.kt (1)
- app/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/signals/DomainSimilaritySignalTest.kt (1)
- app/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/signals/FrequencySignalTest.kt (1)
- app/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/signals/RecencySignalTest.kt (1)
- app/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/signals/TitleKeywordSignalTest.kt (1)
- app/src/test/java/org/mozilla/fenix/components/tabgroupsuggestions/store/TabGroupSuggestionReducerTest.kt (1)
Review comments generated (1)
- Review: 1
- LGTM: 0
Tips
Chat with DevzyAi Bot (@DevzyAi)
- Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
- Invite the bot into a review comment chain by tagging
@DevzyAiin a reply.
See More
Interact with @DevzyAi in any bot review thread (Files changed tab):
| Command | Description |
|---|---|
@DevzyAi explain |
Get a detailed explanation of the code or issue |
@DevzyAi fix |
Generate a code fix suggestion |
@DevzyAi suggest |
Get alternative implementations |
@DevzyAi ignore |
Mark this as a false positive |
@DevzyAi review |
Trigger a full PR review (overrides ignore) |
@DevzyAi test |
Generate unit tests for file(s) |
@DevzyAi help |
Show this help message |
Code suggestions
- The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
- You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.
Pausing incremental reviews
- Add
@DevzyAi: ignoreanywhere in the PR description to pause further reviews from the bot.
Models: code-review → gpt-5.2 · summary → gpt-5-nano · security → claude-test-1782398442901-008bcaf53811e
| repository = tabGroupSuggestionRepository, | ||
| telemetry = TabGroupSuggestionTelemetry(), | ||
| scope = MainScope(), | ||
| ), | ||
| ), |
There was a problem hiding this comment.
🟢 Low
Unmanaged MainScope in singleton store middleware can leak work beyond UI lifecycle
Components creates a new MainScope() for TabGroupSuggestionMiddleware but never stores/cancels it. Since Components is effectively process-long-lived, this scope can keep coroutines running even after the UI feature stops, and it’s not tied to any lifecycle. Cross-file, HomeFragment wires the feature with viewLifecycleOwner.lifecycleScope, implying lifecycle-bound work, but middleware work launched on MainScope() will outlive that.
Prefer injecting a managed, app-level scope (owned/cancellable) or reusing an existing scope that is explicitly cancelled, and avoid creating anonymous scopes inline.
Introduces a suggestion engine that recommends grouping a user's currently open tabs based on independent signals: shared domain, recency, visit frequency, and shared title keywords. Signals are scored with tunable weights, ranked, deduplicated and capped into a small set of suggestions.
Suggestions are surfaced as a dismissible card on the home screen and as a snackbar action from the tabs tray. Dismissed suggestions and an opt-out toggle persist across restarts via a DataStore-backed preferences store. Glean events record when a suggestion is shown, accepted, or dismissed.
Adds:
Pull Request checklist
QA
To download an APK when reviewing a PR (after all CI tasks finished running):
Checksat the top of the PR page.firefoxci-taskclustergroup on the left to expand all tasks.build-debugtask.View task in Taskclusterin the newDETAILSsection.GitHub Automation
Used by GitHub Actions.