github stevezau/shortlist v0.1.0-beta.3
v0.1.0-beta.3 — picks that resemble what you watched

latest releases: v1.8.0, v1.7.0, v1.6.1...
pre-releaseone month ago

Third beta. Mostly one thing, done properly.

Picks that actually resemble what you watched

A beta user's row seeded by The Pitt — a medical drama — came back as The Sandman, Servant,
Torchwood and King & Conqueror, each captioned "Because you watched The Pitt".

TMDB wasn't the problem. Its recommendations for that show are ER, Chicago Med, Grey's Anatomy,
Code Black, Transplant, The Good Doctor — exactly right. Shortlist was reading the right list and
picking from the wrong end of it:

  • TMDB's ordering was being thrown away. Suggestions were pooled into one bag, so "the #1
    closest match" and "#19, loosely related" arrived indistinguishable — and /similar (keyword
    matching, noisy) counted the same as /recommendations (what people actually watch together).
  • Ranking never asked whether a title was similar, only how well-rated it was. On the real data
    that put Traitors — a reality competition show — at the top of a medical drama's row.
  • Genre coherence. Position alone wasn't enough: TMDB tags The Pitt simply "Drama", as it does
    nearly everything it suggests. But Torchwood and The Sandman are also "Sci-Fi & Fantasy", and
    that foreign genre is the whole difference.

Verified against the live TMDB response for that show, not a fixture:

before:  Traitors, The Good Doctor, New Amsterdam, Queer as Folk, Big White Duel
after:   Chicago Med, ER, Grey's Anatomy, Code Black, Transplant

Sources with no ranking of their own — discover, Trakt, the AI sources — are unaffected. They're
deliberate picks, not the tail of a list.

Rows can be short now

Filling a half-empty row from the tail is how a weak association became a delivered title. Padding
now draws only from candidates that are genuinely related, so a row may come up short — four
titles that fit beat ten where six are filler. The run log says so, naming the closest rejected
title, so a short row reads as the filter working rather than a failure.

Where every pick came from

Each pick now records the source that surfaced it and how strongly that source vouched:

#3  The Sandman — Because you watched The Pitt
    suggested by TMDB · loosely related

Nothing claims a strength it didn't measure: sources that don't rank their suggestions say only who
suggested it. The run log carries the same per row at DEBUG — every pick with its seed, source and
affinity — so "why did it pick that?" is answerable from a downloaded log.

Build and logs

  • Faster images. The Docker dependency layer sat below the source copy, so every commit
    reinstalled FastAPI, SQLAlchemy, three LLM SDKs and Pillow from scratch on both architectures. A
    source-only rebuild is now a cache hit. The web stage builds natively instead of under arm64
    emulation.
  • Quieter runs. filters merged printed the entire before and after filter string — ~8 KB per
    user per field on a 48-user server, the same labels every time. It now reports only what changed;
    the full diff still goes to the audit event.
  • Release tags publish :dev alongside :latest and the version tag.

Upgrading

Pull and recreate — migrations run on boot. Expect some rows to be shorter than before; that's the
filter working. Nothing to do by hand.

Full detail in CHANGELOG.md.

Don't miss a new shortlist release

NewReleases is sending notifications on new releases.