HOTNITE MVP / implementation handoff

Build the call loop first.

Goal: a user fills out preferences, HOTNITE selects 3 LA events, calls at a scheduled time, accepts basic voice commands, and texts the selected links.

City: Los Angeles only Mode: cyborg concierge Data: manual first, scrape later Success: real phone rings

0. MVP scope

Must work

  • Preference intake form
  • Manual event entry
  • Rank 3–5 events
  • Outbound call
  • Commands: NEXT, NO, REPEAT, GO BACK, YES, TEXT ME, STOP
  • SMS follow-up

Manual allowed

  • Event curation
  • Source checking
  • Vibe tags
  • Admin corrections
  • Quality control

Do not build yet

  • Autonomous ticket buying
  • Autonomous ride booking
  • Account billing
  • Multi-city support
  • Full scraping network
  • Social graph

1. Recommended stack

LayerUseReason
FrontendNext.js / VercelFastest route to forms, API routes, deployment.
DatabaseSupabase PostgresHosted Postgres, easy JS client, admin table visibility.
Voice + SMSTwilio Programmable Voice + MessagingOutbound call and SMS primitives exist.
Voice brainOpenAI Realtime or server-side LLM with Twilio media/controlUse tool calls/state machine. Do not freewheel.
JobsVercel Cron / Supabase scheduled job / simple workerTrigger scheduled calls.
MapsGoogle Maps links firstNo Maps API needed for MVP.

Principle: boring web app + phone integration + strict state machine. Avoid clever architecture.

2. Minimal database schema

users
- id uuid pk
- phone text unique not null
- name text
- neighborhood text
- home_lat numeric null
- home_lng numeric null
- radius_miles int default 5
- max_budget_usd int null
- preferred_call_time time
- timezone text default 'America/Los_Angeles'
- active boolean default true
- created_at timestamptz default now()

taste_profiles
- id uuid pk
- user_id uuid references users(id)
- likes text[]
- hates text[]
- hard_no text[]
- vibe_positive text[]
- vibe_negative text[]
- mobility_notes text
- notes text
- updated_at timestamptz default now()

events
- id uuid pk
- title text not null
- venue text
- address text
- neighborhood text
- starts_at timestamptz not null
- ends_at timestamptz null
- price_min int null
- price_max int null
- ticket_url text
- source_name text
- source_url text
- categories text[]
- vibe_tags text[]
- hard_no_tags text[]
- description text
- curator_notes text
- status text default 'candidate' -- candidate/approved/rejected/expired
- created_at timestamptz default now()

recommendations
- id uuid pk
- user_id uuid references users(id)
- event_id uuid references events(id)
- call_session_id uuid null
- rank int
- score numeric
- match_reasons text[]
- risk_flags text[]
- status text default 'queued' -- queued/presented/skipped/selected/texted
- created_at timestamptz default now()

call_sessions
- id uuid pk
- user_id uuid references users(id)
- twilio_call_sid text null
- status text default 'scheduled' -- scheduled/active/completed/failed
- scheduled_for timestamptz
- started_at timestamptz null
- ended_at timestamptz null
- current_index int default 0
- transcript jsonb default '[]'

call_events
- id uuid pk
- call_session_id uuid references call_sessions(id)
- event_type text -- agent_said/user_said/command/tool/sms/error
- payload jsonb
- created_at timestamptz default now()

feedback
- id uuid pk
- user_id uuid references users(id)
- event_id uuid references events(id)
- signal text -- yes/no/next/more_like_this/less_like_this/too_far/too_expensive
- notes text
- created_at timestamptz default now()

3. End-to-end flow

User fills intake.

Phone, neighborhood, radius, budget, categories, hates, hard no tags, preferred call time.

Admin enters events.

Manual entry. 30–50 approved LA events. Tag heavily.

Scheduler creates call session.

At call time, rank events for user and create recommendation rows.

Twilio places outbound call.

Call connects to voice route. Voice agent receives pre-ranked list and state.

Agent presents one event at a time.

Short description. Then waits for command. No rambling.

User commands mutate state.

NEXT increments index. GO BACK decrements. REPEAT replays. YES/TEXT ME stores selection.

SMS sends selected events.

Title, time, address, price, ticket/source link, map link.

Feedback updates profile.

Every NO/NEXT/YES becomes future ranking signal.

4. Voice command contract

User saysSystem actionPersist
NO / NEXTSkip current event. Present next.feedback.signal = next
REPEATReplay current event summary.call_events entry
GO BACKMove to previous event. Replay.call_events entry
YESMark current event selected. Offer text link.recommendation.status = selected
TEXT MESend SMS for current or selected events.recommendation.status = texted
MORE LIKE THISPositive taste signal. Continue.feedback.signal = more_like_this
LESS LIKE THISNegative taste signal. Continue.feedback.signal = less_like_this
STOP / BYEEnd call gracefully.call_sessions.status = completed

Agent rules

5. Ranking engine v0

No ML. Deterministic scoring + optional LLM explanation.

score_event(user, profile, event):
  score = 0

  if event.category in profile.likes: score += 20
  if event.category in profile.hates: score -= 40

  score += 8 * count(overlap(event.vibe_tags, profile.vibe_positive))
  score -= 12 * count(overlap(event.vibe_tags, profile.vibe_negative))
  score -= 100 * count(overlap(event.hard_no_tags, profile.hard_no))

  if event.price_max > user.max_budget_usd: score -= 25
  if event.distance_miles > user.radius_miles: score -= 30
  if event.starts_at too soon: score -= 10
  if event.starts_at too late: score -= 10

  if event.source_name in trusted_sources: score += 5
  if curator_notes contains "strong": score += 10

  return score

Output shape

{
  "event_id": "uuid",
  "rank": 1,
  "score": 72,
  "match_reasons": ["nearby", "cheap", "matches repertory film", "not crowded"],
  "risk_flags": ["starts in 70 minutes"]
}

6. LA source map

Do not automate all sources in v0. Use these as admin inputs.

Structured / API-ish

  • Ticketmaster Discovery API
  • Eventbrite-like listings
  • Venue calendars with clean markup
  • Google Maps links for address handoff

Taste layer / manual first

  • Scenestar
  • Restless Nites
  • RA / DICE / Songkick-style music listings
  • American Cinematheque / New Beverly / Los Feliz 3 etc.
  • Gallery newsletters
  • Eater LA / Infatuation openings
  • Instagram flyers / venue posts

7. Minimal routes

POST /api/intake
- create user + taste_profile

POST /api/admin/events
- create/update event

POST /api/recommendations/generate
- user_id + date window -> recommendation rows

POST /api/calls/schedule
- user_id + scheduled_for -> call_session

POST /api/calls/start
- call_session_id -> Twilio outbound call

POST /api/voice/webhook
- Twilio webhook / call events

POST /api/voice/command
- parsed command -> update call session state

POST /api/sms/send-selection
- send selected event details

POST /api/feedback
- persist taste signal

8. Admin pages

/admin/events

  • Event create/edit table
  • Status: candidate / approved / rejected / expired
  • Required fields validation
  • Fast tagging UI

/admin/calls

  • Upcoming scheduled calls
  • Manual “call now” button
  • Transcript viewer
  • SMS resend button

9. Build order

  1. Create Next.js app and deploy empty shell.
  2. Create Supabase project and schema.
  3. Build intake form.
  4. Build admin event entry.
  5. Seed 30 fake/manual events.
  6. Build deterministic ranking function.
  7. Build recommendation preview page: user → ranked events.
  8. Build call session state machine without Twilio.
  9. Connect Twilio outbound call.
  10. Connect voice command parsing.
  11. Connect SMS follow-up.
  12. Run 20 calls on one test user.

10. Scope cuts / anti-waste rules

11. Acceptance criteria

MVP complete when: one real user gets one useful call and one useful SMS from manually curated LA events.

12. Env vars

SUPABASE_URL=
SUPABASE_SERVICE_ROLE_KEY=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
TWILIO_PHONE_NUMBER=
OPENAI_API_KEY=
ADMIN_SECRET=
APP_BASE_URL=

13. One-sentence assignment

Build a Next.js + Supabase + Twilio prototype where a scheduled outbound call reads ranked LA event recommendations, obeys NEXT/REPEAT/GO BACK/TEXT ME, and sends SMS links.