whats-on
βlive active personalTV show tracker - What's On tonight?
- Primary: https://whats-on.onrender.com
- GitHub: https://github.com/klill6506/whats-on
- Local:
D:\Personal\whats-on
README
# What's On? πΊ
A simple, mobile-first TV show tracker. No automatic syncing, no clutter from shared accounts β just your shows.
## Features
- π’ **Ready to Watch** β Shows you're current on with new episodes
- β³ **Catching Up** β Shows you're behind on
- π± **Mobile-first** β Designed for phone/iPad
- π¦ **Henry Integration** β Tell Henry what you watched, he updates the tracker
## Tech Stack
- FastAPI + Jinja2
- SQLite
- Tailwind CSS (CDN)
- Deployed on Render
## API Endpoints
- `GET /` β Main dashboard
- `GET /api/shows` β List all shows
- `POST /api/shows` β Add a show
- `PUT /api/shows/{id}` β Update a show
- `DELETE /api/shows/{id}` β Delete a show
- `POST /api/shows/{id}/caught-up` β Mark show as caught up
## Local Development
```bash
pip install -r requirements.txt
python main.py
```
Then open http://localhost:8005
# Trigger redeploy
STATUS
---
type: project-status
project: What's On
last_updated: 2026-06-04
---
# STATUS β What's On
*The freshest file. Answers "where am I on this project?" Updated at the end of every substantive session.*
---
## Current state
**Recommender rebuild (Phases 1β6) is merged to `main` and pushed** (merge `e6fdb1d`,
pushed 2026-06-05) β Render is redeploying. See [RECOMMENDER_REBUILD.md](RECOMMENDER_REBUILD.md).
22 unit tests passing. Working tree is on `main`; the `recommender-rebuild` branch still
exists locally (can be deleted).
What shipped: `show_tags` + AI tagger (P1-2); signed taste profile + cosine scoring,
`random.random()` and popularity bonus removed, deterministic ranking (P3-4); math-based
why/why-not + cached Henry-voice verdict/risk/plan (P5); match badge + review on the cards
(P6); plus background (non-blocking) cache refresh so page loads never wait on the API.
## In progress / post-deploy checklist (Ken must do these β CC can't)
- [ ] **Set `ANTHROPIC_API_KEY` in the Render dashboard** β required for tagging/reviews
in prod. Not in render.yaml; must be added as a dashboard env var.
- [ ] **Confirm `TMDB_API_KEY` is in Render** β recs are hidden without it (service filter).
- [ ] **After deploy, POST `/api/admin/retag` once on the live site** to tag the library on
the Render DB (idempotent, pennies).
- [ ] **Push-away demo:** add a genuine 1β2 rating on a sentimental show (adding `This Is Us`
at rating 1 is the clean test; baseline: it scored 97), refresh, confirm sentimental
candidates drop.
## Next up
1. Verify the live site after Render finishes deploying + the steps above.
2. (Optional) `seed_tags.json` so a fresh Render DB isn't empty without the retag call.
3. (Optional) delete the merged `recommender-rebuild` branch.
## Known issues
- Recs require `TMDB_API_KEY` (service filter) β invisible locally without it.
- ~6/40 reviews can be `None` after a refresh if Anthropic returns 529; they regenerate
on the next refre
β¦(truncated for upload size)
DECISIONS
--- type: project-decisions project: What's On last_updated: 2026-06-04 --- # DECISIONS β What's On *Architectural and scope choices. Append-only log. Each entry is a decision that shouldn't be re-litigated without new information. If you find yourself reopening a decision, either add a new entry that overrides the old (and say why) or leave both so the history is visible.* --- ## How to use this file Each decision gets a dated entry with: what was decided, why, what was considered instead, and what would change our mind. Never delete entries β if a decision is reversed, add a new one that supersedes it. --- ## 2026-06-05 β Recommender: taste-dimension model with deterministic cosine scoring **Decision:** Replace the genre-based recommender with an 11-dimension taste model. Shows/candidates are tagged 0β5 on each dimension (AI, stored in `show_tags`); a signed taste profile is built from Ken's ratings (`weight = rating β 3`); candidates are scored by cosine similarity to that profile, mapped to 0β100. AI is used only for tagging and for the Henry-voice verdict/risk/plan; the match score and the why/why-not lines are pure math. **Context:** The old engine used Trakt genres only (too coarse), let dislikes add instead of subtract, and injected `random.random()` into the score (noisy, unstable rankings). **Alternatives considered:** Embeddings/ML similarity β rejected; the transparent, explainable dimension model is the point. Keeping AI in the scoring path β rejected; scores must be deterministic and trustworthy, so AI is confined to tagging and prose. **Reasoning:** Signed weights make dislikes actively push away along the axes a disliked show scores high on. Cosine + no random = stable, reproducible rankings (verified identical across back-to-back refreshes). Math-based explanations are cheap and auditable. **Would reconsider if:** The dimension set proves too coarse to separate shows Ken rates very differently, or single-user assumptions break (multi-use β¦(truncated for upload size)
MEMORY
---
type: project-memory
project: What's On
last_updated: 2026-06-04
---
# MEMORY β What's On
*Standing facts, preferences, and accumulated context. Long-lived β not "what I did yesterday" (that's STATUS.md). Update when you learn something worth keeping.*
---
## Purpose and scope
A personal TV-show tracker for Ken β single user, no login. Answers "what's on / what
should I watch next?" on a phone or iPad. Tracks watch progress, surfaces taste-aware
recommendations, and pulls air days/posters from external APIs. Standalone personal
app, not part of the tax suite.
## Domain knowledge
- **Show categories on the home page** (computed in `main.py:home`):
- `between_seasons` β `status == 'hiatus'`
- `backup_shows` β `priority == 3` ("watch if nothing else is on")
- `catching_up` β `current_episode != 99` and `status == 'watching'`
- `priority_shows` β everything else (the "ready to watch" hero list)
- **`current_episode = 99` is a sentinel meaning "caught up"** β not a real episode
number. Lots of logic keys off this; don't treat 99 as data.
- **Statuses:** `watching`, `current`, `hiatus`, `dropped` (`VALID_STATUSES`).
`dropped` shows are hidden from `get_all_shows()`.
- **Priority** 1β5 (1 = highest). **Rating** 1β5 (drives recommendation weighting;
unrated defaults to 2).
- **Services** Ken uses (`USER_SERVICES`): Max, Apple TV+, Hulu, Peacock, Paramount+,
Prime Video, Netflix. Plus Disney+/Other allowed as `VALID_SERVICES`.
## User preferences discovered
- Wants the standard four project files (CLAUDE/MEMORY/STATUS/DECISIONS) maintained and
kept current as the repo changes β established 2026-06-04 when cloning the repo local.
- Mobile-first, low-clutter UI. Brand-colored service badges, "yeti/Henry" mascot.
## Integrations and external systems
- **Trakt API** (`api.trakt.tv`) β show search, full details (air day/time, genres,
next episode), and `related` shows for collaborative recommendations. Auth via
`trakt-api-key` header; client I
β¦(truncated for upload size)
CLAUDE.md
# CLAUDE.md β What's On
*Project-specific rules. User-level conventions come from `~/.claude/CLAUDE.md` β do not duplicate here.*
---
## What this is
"What's On?" β a personal, mobile-first TV show tracker for Ken. Tracks what he's
watching, what he's caught up on, what's on hiatus, and surfaces taste-aware
recommendations. Single-user, no auth, no clutter. **This is a standalone personal
app β NOT part of the Sherpa tax suite.**
## Tech stack
- **Backend:** FastAPI (Python) β `main.py` (routes) + `database.py` (data layer)
- **Frontend:** Server-rendered Jinja2, single template `templates/index.html`; Tailwind CSS via CDN
- **Database:** Dual-mode β SQLite locally, PostgreSQL (psycopg3) on Render when `DATABASE_URL` is set
- **Hosting:** Render (`render.yaml`); SQLite on a 1GB persistent disk by default
- **Dependencies:** pip (`requirements.txt`) β not Poetry
## Session startup
Read all four root files (CLAUDE.md, MEMORY.md, STATUS.md, DECISIONS.md) at the
start of substantive work. No other required reading.
## Conventions
- This app does **not** inherit the Sherpa-suite database/color rules from
`~/.claude/CLAUDE.md`. Specifically:
- **No shared Supabase DB.** It owns its own SQLite/Postgres database. No `firm_id`,
no central `clients` table.
- **No tax data-entry color system** (red/yellow/green). UI colors here are
service-brand colors (Max, Hulu, etc.) in `index.html`.
- Tests, if added, go in `tests/` as `test_{module}.py` (pytest) β per global rules.
- Keep the SQLite/Postgres dual-path working: `database.py` branches on `DATABASE_URL`.
Any new query must use the `_ph()` placeholder helper so it works on both engines.
## Do not redesign without asking
- The category logic on the home page (priority / backup / catching-up / between-seasons)
and the `current_episode = 99` "caught up" sentinel. Both are load-bearing.
- The recommendation engine scoring in `refresh_recommendation_cache()` β taste-aware,
genre-weighted, collaborat
β¦(truncated for upload size)
Diary mentions
No recent diary mentions for this app.
Render
- Service:
whats-on - Status: live
- Last deploy: 2026-06-05T16:36:20.975428Z