Skip to content

feat(rank): personalized ranking that learns from your clicks (Android A1)#48

Merged
ErikChevalier merged 1 commit into
mainfrom
feat/click-personalization
Jun 2, 2026
Merged

feat(rank): personalized ranking that learns from your clicks (Android A1)#48
ErikChevalier merged 1 commit into
mainfrom
feat/click-personalization

Conversation

@ErikChevalier
Copy link
Copy Markdown
Contributor

What

Ports the desktop click-personalization feature to Android: in-app learning + owner-gated server apply + the opt-in UI, with a model and JSON format identical to the desktop app (already shipped in SearchMob-Desktop 26.06.00) so a learned profile moves between devices. This is A1; A2 will add owner-only learning from clicks on the served browser page (the /click route).

Algorithm (parity with desktop)

  • Beta-Bernoulli per-domain and per-(query-term × domain) click model, fed by the position-bias-resistant "clicked over skipped-above" signal.
  • Bounded boost (0.5×–2×, so engine consensus stays primary and pin/raise/lower/block always win), epsilon-greedy exploration, cold-start gates, time decay, and eviction caps.
  • Serializes to the portable beta_bernoulli_v1 JSON. A cross-platform fixture test loads a model produced by the desktop Python code and reproduces its exact boosts.

Privacy / safety

  • Model stored encrypted alongside ranking rules (ranking.personalization); absent without error when the vault is locked.
  • Owner-only: in-app results are always the owner's; the served path personalizes only for the loopback owner, so a network visitor gets engine order and never the owner's bubble.
  • Off by default, offered as a recommended setup-wizard step and a Settings toggle, with Export / Import / Reset. The wizard now re-appears once after a feature update (onboarding-version gate).

Seams

Apply pass in MetaSearchResultProvider (between sort and DomainRanker); native training via SearchViewModel.onResultOpened; opt-in flag in PreferencesRepository; UI in the onboarding wizard + SettingsScreen.

Tests

ktlint, lint, assembleDebug, and unit tests green. New: full model math/skip-above/decay/caps/clamp/cold-start/epsilon/JSON round-trip + the cross-platform desktop fixture; ViewModel native-training (records when enabled, nothing when off); updated the onboarding page-count and provider-fake signatures.

🤖 Generated with Claude Code

…d A1)

Port the desktop click-personalization feature to Android (in-app learning,
owner-gated server apply, and the opt-in UI), with a model and JSON format
identical to the desktop app so a profile moves between devices.

- engine/rank/Personalization.kt: a Beta-Bernoulli per-domain and
  per-(query-term x domain) click model fed by the position-bias-resistant
  "clicked over skipped-above" signal. Bounded boost (0.5x to 2x),
  epsilon-greedy exploration, cold-start gates, time decay, and eviction
  caps. Serializes to the portable beta_bernoulli_v1 JSON; a cross-platform
  fixture test loads a desktop-produced model and reproduces its boosts.
- data/prefs/PersonalizationPreferences.kt: persists the model encrypted in
  the same store as the ranking rules (key ranking.personalization),
  fail-soft and absent when the vault is locked.
- Apply pass runs in MetaSearchResultProvider between the sort and
  DomainRanker (so PIN/RAISE/BLOCK win). In-app results always personalize;
  the served path only does so for the loopback owner, so a network visitor
  gets engine order and never the owner's bubble.
- Native clicks train the model via SearchViewModel.onResultOpened, using
  the displayed order for the skip-above signal; only when enabled.
- Opt-in and recommended: a setup-wizard step and a Result-ranking settings
  toggle, plus Export / Import / Reset of the portable model. The wizard now
  re-appears once after a feature update (onboarding_version gate).

A2 will add owner-only learning from clicks on the served browser page.

Gate green: compileDebug, ktlint, lint, assembleDebug, unit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ErikChevalier ErikChevalier merged commit b868f66 into main Jun 2, 2026
2 checks passed
@ErikChevalier ErikChevalier deleted the feat/click-personalization branch June 2, 2026 07:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant