Skip to content

feat(rank): personalized ranking that learns from your clicks (D1)#65

Merged
ErikChevalier merged 3 commits into
mainfrom
feat/click-personalization
Jun 2, 2026
Merged

feat(rank): personalized ranking that learns from your clicks (D1)#65
ErikChevalier merged 3 commits into
mainfrom
feat/click-personalization

Conversation

@ErikChevalier
Copy link
Copy Markdown
Contributor

What

Adds an opt-in, on-device learning layer that nudges ranking from the owner's own result clicks and gets better the more they search, without breaking the store-nothing, owner-safe privacy posture or parity with the Android app. This is PR D1 of the feature (core model + native-app learning + opt-in UI); a follow-up D2 adds owner-only learning from the served browser page, then the Android parity PRs.

This PR also adopts OpenSpec for the desktop repo and scaffolds the validated add-click-personalization change (proposal / design / specs / tasks).

Algorithm

  • Beta-Bernoulli per-domain and per-(query-term × domain) click model, fed by the position-bias-resistant "clicked over skipped-above" signal: the clicked result's host gains a click, each distinct host shown above it that was skipped gains a skip, hosts below are ignored.
  • Bounded boost clamped to 0.5×–2× (engine consensus stays primary; explicit pin/raise/lower/block rules always win), with epsilon-greedy exploration, cold-start gates, time decay (60-day half-life), and size caps with least-observed eviction.
  • Serializes to a portable JSON model (beta_bernoulli_v1) with byte-identical keys/floats so it interops with the upcoming Android port.

Privacy / safety

  • Model stored encrypted in the vault (ranking.personalization); absent without error under a locked or zero-knowledge vault, like ranking rules.
  • Owner-only: native app clicks train it; on the served path it applies only for the loopback owner. Network/LAN clients get the un-personalized order and never train it. The MCP agent-safety scope is untouched.
  • Off by default, offered as a recommended setup-wizard step and a Settings toggle, with Export / Import / Reset. The wizard copy is honest about the residual local-device risk.

Also

  • The setup wizard now re-appears once after a feature update (framed as "What's new") via an onboarding_version gate, so existing users discover new opt-in features instead of only fresh installs seeing them.

Tests

ruff + ruff format + mypy clean; 547 pytest passed (25 new: model math/skip-above/decay/caps/clamp/cold-start/epsilon/JSON round-trip, vault store round-trip, settings + wizard persistence, and a served-route owner-vs-network gating test).

🤖 Generated with Claude Code

FlintWave and others added 3 commits June 1, 2026 22:49
Initialize OpenSpec for the desktop repo (config.yaml with the project
context, Claude commands/skills) and scaffold the validated
add-click-personalization change (proposal, design, specs, tasks) for the
adaptive click-personalized ranking feature.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an opt-in, on-device learning layer that nudges ranking from the
owner's own result clicks and improves the more they search, while
honoring the store-nothing, owner-safe privacy posture.

- engines/rank/personalize.py: 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 multiplicative boost
  (clamped 0.5x to 2x), epsilon-greedy exploration, cold-start gates, and
  time decay so it never collapses result diversity or acts on weak
  evidence. Serializes to a portable JSON model (beta_bernoulli_v1) shared
  with the Android app.
- data/personalization_store.py: persists the model encrypted in the vault
  (key ranking.personalization), fail-soft and absent under a locked or
  zero-knowledge vault, like ranking rules.
- Apply pass runs between sort_results and apply_ranking, so explicit
  pin/raise/lower/block rules always win. In the served path it applies
  only for the loopback owner; network visitors get the un-personalized
  order and never train the model. The MCP scope is untouched.
- Native GUI clicks train the model via a new ResultsView.resultActivated
  signal and the displayed result order.
- Opt-in and recommended: a setup-wizard step and a Result-ranking
  settings toggle, plus Export / Import / Reset of the portable model.
- The setup wizard now re-appears once after a feature update (framed as
  "What's new") via an onboarding_version gate, so existing users discover
  new opt-in features.

Gate green: ruff + ruff format + mypy + 547 pytest. Scaffolds the
add-click-personalization OpenSpec change (desktop repo adopts OpenSpec).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ErikChevalier ErikChevalier merged commit 7da5138 into main Jun 2, 2026
1 check passed
@ErikChevalier ErikChevalier deleted the feat/click-personalization branch June 2, 2026 06:40
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