Files
Bike-Tinder/docs/RANKING.md

3.1 KiB
Raw Blame History

RANKING — Bike-Tinder

The deck is ordered by predicted taste. Three rungs, in order: the hunt's existing rubric (v1), a cheap explainable boost from swipe clusters (v1.x), then a small learned model (Phase 3). Nothing ever becomes a black box.

Signals

  • Like = strong positive (+1). Maybe = weak positive (+0.25, kept in its own bucket). Nope = negative (−1). List-view edits count identically to in-deck swipes; the latest call wins.
  • Planned v2 signals: dwell time on card, "open listing" click-outs.

Features (v1)

Per listing: family tier (from the hunt rubric), displacement (cc), price, year, km, km/year, dealer vs private, place/region, keyword flags from title + description (MVI/inspection, whitewalls, spokes, windshield, saddlebags, ABS, bobber/custom), color words (red is known lore), title tokens (make/model). Numeric features normalized; missing values neutral.

  • Single-source v1 makes source/region features near-constant — kept in the schema for v2.

Stage 1 — Cold start

Order by the hunt's existing rubric score (family tiers + MVI / whitewalls / windshield / spokes / ABS bonuses + price fit). This is already explainable (reason strings exist in the hunt system). Deck = rubric order until ≥ 20 swipes.

Stage 1.5 — Cheap swipe boost (v1.x, post-launch)

Before any model exists, the importer can nudge score with plain swarm signals — e.g. +N when recent likes cluster in a family, small time-decay otherwise — recomputed each import, zero ML, fully explainable. Ships as v1.x if Glenn wants it; v1 itself stays pure rubric.

Stage 2 — Learned order (Phase 3 roadmap)

  • Model: logistic regression on the features above; target = like vs nope (maybes contribute softly positive). Trained on all swipe history including edits.
  • Pure-Python or numpy implementation (tiny data — no heavy ML dependency). Params + training metadata live in the DB; versioned.
  • Retrain nightly right after import; on-demand retrain available from the app.
  • Guardrails:
    • Blend: until 100 swipes, deck order = 50% rubric rank + 50% model rank; after, model order with an exploration slice (roughly every 8th card = top-rubric card not yet shown).
    • Never hide: everything stays in List; ranking only reorders the deck.
    • Sanity: if model like-rate underperforms the rubric baseline (tracked weekly), it reverts to rubric order silently.

Explainability

Each card can show up to 2 chips: top positive contributions for that bike ("low km for the money", "Vulcan family", "red", "MVI"). Chips derive from the live feature contributions; during cold start they come from the rubric's existing reasons.

Evaluation

  • Track: swipes/session, like-rate trend, maybe-rate, undo rate, and "liked-item rank" (how deep liked items sat — should trend shallow).
  • Notifications (decided 2026-11-10): in-app "new since last visit" badge only — no Conduit pushes. A weekly digest remains possible later if asked for.

Open decisions

  • Maybe-handling: weak positive + separate bucket (proposed) vs pure third bucket — confirm with Glenn at Phase 2.
  • Distance feature (map-based proximity) — likely v2.