Five stations, one training set

Because real archetype-labeled viewing data doesn't exist at scale, MovieTwirl generates it — grounded in affinity scores, constrained by viewing sociology, and exported as parquet for ML.

Station 01

Build affinities

Score every movie against King, Warrior, Magician, Lover using a reference corpus and zero-shot NLI classifiers (BART-MNLI).

affinities.parquet
Station 02

Sample users

500 synthetic users with Dirichlet-sampled archetype weights, hero journey stage, and demographics.

users.parquet
Station 03

Sample sessions

8–20 sessions per user with viewing mode (solo, compromise, kids_win, impress, obligation) and 10 affinity-aware candidates.

sessions.parquet
Station 04

Join decisions

Softmax-sample one chosen title per session; add counterfactual top-K rows; attach satisfaction scores.

decisions.parquet
Station 05

Enrich rationales

Join user profiles + movie signatures; optional LLM rationales on chose rows (Anthropic, Ollama, or Poolside).

v1.parquet

Station 4 — decision schema

Each session produces surfaced rows (what the UI showed) plus counterfactual rows (what would have ranked higher).

session_id, user_id, mode, movie_id,
surfaced (bool), chose (bool),
satisfaction (1–5 | null), score (float)

• Exactly one surfaced row has chose=True (softmax, temp=0.3)
• satisfaction only on the chosen row
• 3 counterfactual top-K rows per session (surfaced=False)

Viewing modes

Mode shifts the effective taste function — same user, different social context, different choice.

  • solo — Personal pick — hardest to predict (12.4% hit@1)
  • compromise — Household balance between profiles
  • kids_win — Family-friendly override
  • impress — Social performance — highest hit@1 (22.2%)
  • obligation — Commitment viewing (friend's recommendation, etc.)

Config knobs

n_users: 500
sessions_per_user: 8–20
candidates_per_session: 10
counterfactuals_per_session: 3
cohort_frozen: true        # lock after study start
llm_provider: poolside     # rationales (optional)

From parquet to ranker

python -m scripts.synth_data.station_04_join_decisions
python -m scripts.synth_data.station_05_enrich_rationales
python -m scripts.train_archetype_ranker
python -m scripts.archetype_cohort_snapshot

The trainer fits a listwise softmax ranker (v2) on session groups. Rationales are optional enrichment — hit@1 metrics depend only on chose labels and affinity scores.