Skip to content

Latest commit

 

History

History
82 lines (63 loc) · 4.64 KB

File metadata and controls

82 lines (63 loc) · 4.64 KB

AGENTS.md

Project Overview

  • Name: Dualist
  • Stack: SvelteKit 2, Svelte 5 (runes), TypeScript
  • Purpose: MVP that ingests a TMDb public list URL, stores a snapshot of movies in Supabase, lets the user rank via pairwise comparisons, and shows a finished permalink (read-only results).

Setup & Dev Commands

  • Create a .env at project root with: SUPABASE_URL=… SUPABASE_SERVICE_ROLE=… TMDB_V4_TOKEN=… TMDB_IMG_BASE=https://image.tmdb.org/t/p
  • Install deps: npm install
  • Run dev: npm run dev

Key Architectural Decisions & Rationale

Snapshot Approach

Each list item stores a frozen snapshot (title/year/poster_path) in list_items.snapshot. Why: Keeps permalinks stable over time and avoids live TMDb lookups during view. If a movie's title changes on TMDb, your ranked list still shows what you ranked.

Normalized Comparison Data

Uses comparisons and rankings tables instead of JSONB. Why: Better for analytics, querying, and performance. Enables future features like "which movies win most often" across lists.

Bulk Save Pattern

All comparisons stored client-side (with localStorage persistence) and only saved to database when user completes all pairs. Why: Reduces API calls, enables offline support, and provides better UX (no loading states between each choice).

Comparison Shuffling

Comparisons are shuffled using Fisher-Yates algorithm when initialized. Why: Randomizes the order users see pairs, preventing bias from always seeing movies in the same sequence.

Source Tracking

Lists track source_platform, source_url, source_list_id. Why: Designed for future Letterboxd support. The ingest flow is abstracted to make adding new platforms straightforward.

Coding Conventions & Guardrails

Styling

  • No Tailwind or inline styles. Use Svelte component-scoped <style> blocks only.
  • Why: Keeps styles co-located with components and avoids external dependencies.

Secrets & Security

  • Keep secrets server-only: Never expose SUPABASE_SERVICE_ROLE or TMDB_V4_TOKEN to the browser.
  • All Supabase writes/reads performed in server code (actions, loads, +server routes) with the service role client.
  • TMDb fetching is server-only (in lib/server/tmdb.ts or via a server function).
  • Why: Prevents credential leakage and ensures all data access is controlled server-side.

Error Handling & Navigation

  • Use throw redirect(...) and throw error(status, msg); do NOT catch and swallow these.
  • Don't wrap redirects inside try/catch unless you rethrow framework exceptions.
  • Client navigation in comparison flow uses window.location for reliability after bulk saving (redirects to results page).
  • Why: SvelteKit's throw pattern is the framework's way of handling redirects/errors. Catching them breaks the framework's flow.

TypeScript & Types

  • TypeScript everywhere; keep types close to usage (define Progress, load return types).
  • Why: Type safety catches errors early and makes refactoring safer.

Accessibility

  • Use interactive elements for clicks in the UI (e.g., <button> for comparison cards).
  • Why: Required for keyboard navigation and screen readers.

Database Schema Notes

  • RLS: OFF for MVP (all DB access is server-side with service role). Can be enabled later.
  • lists.progress: Legacy JSONB field (will be replaced by normalized tables). Don't use for new features.
  • films table: Not used in MVP. Can be added later to dedupe and enable analytics; easy to backfill from existing snapshots.

TMDb Usage

  • Use TMDb v4 list API with Bearer Read Access Token (TMDB_V4_TOKEN) to fetch public lists.
  • Only ingest movie media_type for MVP.
  • Store only poster_path (build URLs in UI with https://image.tmdb.org/t/p/w342{poster_path}).

Immediate Roadmap

  • Letterboxd integration: Add support for importing Letterboxd lists (architecture already supports it via source_platform; need to implement ingest.letterboxd() similar to ingest.tmdb())
  • Better error states: Add +error.svelte for 404s and other errors (currently uses default SvelteKit error page)
  • Optional polish:
    • Debounce/double-click guard and clearer disabled state during submit
    • Remove server/client debug logs before production
    • Add loading states during bulk save operation

Future (not for MVP, but keep in mind)

  • Add films table and backfill from list_items.snapshot to enable cross-list analytics
  • Add ranks table to persist final order per list for easier querying
  • Optional move of ingest into a Netlify Function or Supabase Edge Function; current code is abstracted behind ingestTmdbList to ease that refactor