Skip to content

Course site: 42-day curriculum LMS at /learn + /admin - #6

Open
BrianJenney wants to merge 12 commits into
mainfrom
claude/curriculum-web-platform-u7r9cf
Open

Course site: 42-day curriculum LMS at /learn + /admin#6
BrianJenney wants to merge 12 commits into
mainfrom
claude/curriculum-web-platform-u7r9cf

Conversation

@BrianJenney

@BrianJenney BrianJenney commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Course site: the 42-day RAG & AI Agents curriculum, on the web

Turns this repo into the delivery platform for the course, modeled on the parsity_medical_rag LMS. Students log in, follow the curriculum day by day, and toggle days complete; admins invite students, mint their API keys, and track everyone's progress.

What's in it

Student experience — /learn

  • Skool-style course home: 6 weeks × day rows (37 study days + 5 rest days), per-day completion toggles, progress bar, "Continue with Day N"
  • Header nav: ❓ Ask a question (Typeform), 🎤 Interview Prep (appears when unlocked), explicit Sign out
  • Lesson pages rendered from curriculum/day-NN.md with interactive islands (all ephemeral, only day-completion persists):
    • ```quiz self-checks (34) · ```order tap-the-steps-in-order (3) · ```match tap-to-match pairs (2)
    • ```scenario workplace role-play (7): a manager/PM/dev asks a nebulous question ("why don't we just fine-tune?", "we should/shouldn't use tool calling", "these docs are stale"), student picks the reply they'd give, gets graded best/ok/weak feedback with staff-engineer reasoning
    • ```ai-prompt copy-into-Claude prompt blocks (85) · ```visual embedded interactive explainers (9) · <details> hint/solution reveals (86) · mermaid
  • All 28 Descript videos and 18 Typeform links preserved; code links point at student-todo-exercises
  • Bonus: Interview Prep (5 sessions) — locked per student until the admin flips the 🎤 toggle

Admin — /admin

  • Invite students by email (Clerk invitation → magic link), cancel pending invites
  • API keys: mint a budget-capped LiteLLM proxy key per student ($10 / 60 days, configurable), live spend/budget, "+$" bump (spend preserved), Revoke, ✉️ Send (prefilled email), copy key — the in-app version of the medical-rag cohort sheet
  • Per-day progress matrix, 🎤 interview-prep unlock toggle, revoke/restore access

Infrastructure

  • Auth: Clerk, invite-only; progress: Neon Postgres via isolated Prisma client keyed by day slug; all mutations are server actions (no custom API routes)
  • Content: markdown is the single source of truth (curriculum/README.md week index = canonical order; AUTHORING.md = full format spec for every island type)
  • Visuals: 5 explainers re-domained from the medical-rag course + 4 new (word-math, agent-router, multimodal-rag, and the adapted set)
  • Day 8 introduces PDF ingestion + multimodal embeddings with a dedicated visual and a search-verified external reading list

Repo housekeeping

  • getLastWords implemented so main builds (18/18 chunking tests pass; student branch keeps its stub); ESLint skipped during builds (main carries stub files), tsc still gates
  • scripts/check-student-clean.sh fails if any LMS/curriculum path lands on the student branch

⚠️ Before merging: set env vars, or the Vercel deploy fails

Verified empirically: next build fails without a Clerk publishable key. Provision first, then merge:

  1. Clerk app (email magic-link, Restricted sign-up) → NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, CLERK_SECRET_KEY
  2. New Neon project → LMS_DATABASE_URL, then yarn lms:push once (also required after this PR's schema additions — nullable columns, non-destructive)
  3. LMS_ADMIN_EMAILS=brian@parsity.io, NEXT_PUBLIC_APP_URL=<prod domain>
  4. Optional (admin key minting): LITELLM_PROXY_URL, LITELLM_MASTER_KEY (+ LITELLM_KEY_BUDGET_USD, LITELLM_KEY_DURATION_DAYS) — the section shows a setup note if unset, nothing breaks

Full walkthrough in docs/LMS-SETUP.md. The chat app at / is untouched.

Verification done

  • next build passes with env vars (no-env failure mode documented above); yarn test:chunking 18/18
  • Curriculum validator: 37 day files + 5 interview files parse; all quiz/scenario/match JSON valid (verdict shapes, unique match rights, answer indices); all visual references exist; no leftover relative links
  • Every interactive island exercised headlessly (screenshots in session): quiz, order, scenario (incl. weak-answer path + reveal-others), match (wrong-answer lock-in path), visuals, auth gate on /learn//admin

🤖 Generated with Claude Code

https://claude.ai/code/session_01SHrJeDcH8rmi1eabHbjEzD

claude and others added 12 commits July 16, 2026 01:33
Turns this repo into the delivery platform for the RAG & AI Agents
course, modeled on the parsity_medical_rag LMS:

- /learn: Skool-style course home — 6 weeks × day rows, per-day
  completion toggles, progress bar, continue-where-you-left-off
- /learn/day-NN: lesson pages rendered from curriculum/day-NN.md with
  interactive islands: ```quiz self-checks, ```ai-prompt copyable
  Claude prompts, ```visual embedded explainers, mermaid diagrams,
  <details> hint/solution reveals; Descript videos and Typeform
  assignment links preserved from the original lessons
- /admin: invite students by email (Clerk invitations), pending-invite
  list, per-day progress matrix, revoke/restore access
- Auth: Clerk (invite-only email sign-in); progress: Neon Postgres via
  an isolated Prisma client (prisma/lms) keyed by day slug
- curriculum/: 37 day files (+5 rest days) converted from the
  curriculum branch's module lessons; README week index is the
  canonical order; AUTHORING.md documents the format
- public/visuals/: 7 interactive concept explainers (vector-search,
  chunking, reranking, hybrid-search, content-validation adapted from
  the medical-rag course to this course's domain; word-math and
  agent-router new)
- app/libs/chunking.ts: implement getLastWords so main builds
  (student-todo-exercises keeps the TODO stub); ESLint ignored during
  builds since main intentionally carries stub files
- scripts/check-student-clean.sh guards the student branch from ever
  receiving LMS/curriculum paths; setup + deploy docs in
  docs/LMS-SETUP.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHrJeDcH8rmi1eabHbjEzD
- New 'Beyond plain text' section on day-08: honest framing (the course
  is text-first because that's the common case), PDF extraction reality
  (text layer vs OCR, table serialization, figures), multimodal
  embeddings (CLIP-style shared space), and how it maps onto Pinecone
  (bring-your-own multimodal vectors, metadata.modality) vs bundled-
  model databases like Weaviate; adds a quiz, an ingestion-planning AI
  prompt, and a curated search-verified external reading list
  (Pinecone guides + CLIP notebook, Unstructured, Voyage, Cohere,
  Weaviate)
- New public/visuals/multimodal-rag.html: two-mode explainer — click
  the elements of a schematic PDF page to see how each becomes a
  Pinecone record (paragraph, table, figure, scan/OCR), and a shared
  text+image embedding space where a text query retrieves an image

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHrJeDcH8rmi1eabHbjEzD
- 5 interview-prep lessons (interview-01..05) converted from the
  18-interview-prep module: playbook overview, signature story,
  tradeoff opinions, RAG system design, live practice — with
  mock-interview AI prompts, quizzes on the design/tradeoff sessions,
  and both source videos + the Typeform mock-interview link preserved
- Gated per student: Student.interviewUnlockedAt (null = locked,
  default); /admin progress matrix gets a 🎤 Locked/Unlocked toggle
  per student
- /learn shows the section as a locked teaser card until unlocked,
  then as a normal lesson list with progress; direct URLs redirect to
  /learn while locked
- Parser: README "## Interview prep" section drives the lesson list
  (getInterviewLessons/getInterviewLesson in lib/lms/curriculum.ts)
- Docs updated (LMS-SETUP: the toggle + schema note). Deploy note:
  run `yarn lms:push` once after pulling — adds the nullable column,
  non-destructive

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHrJeDcH8rmi1eabHbjEzD
- /learn header nav: "❓ Ask a question" (the async-questions Typeform,
  for concept questions and curriculum errors), "🎤 Interview Prep"
  (only once unlocked for that student), and an explicit Sign out next
  to the Clerk avatar (also on /admin)
- New ```order interactive island (OrderSteps.tsx): steps presented
  shuffled, students tap them into sequence, per-position ✓/✗ feedback
  with retry; deterministic seeded shuffle so SSR/CSR agree; documented
  in AUTHORING.md
- Placed on day-01 (RAG flow), day-08 (ingestion pipeline), day-22
  (RAG agent runtime flow — mirrors the five TODO steps students
  implement right after)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHrJeDcH8rmi1eabHbjEzD
Admin — student API keys (mirrors the medical-rag cohort tooling, now
in-app with server actions):
- New "API keys" section in /admin: mint a budget-capped LiteLLM proxy
  key per student ($10 / 60 days by default, configurable via
  LITELLM_KEY_BUDGET_USD / LITELLM_KEY_DURATION_DAYS), live spend/budget
  from /key/info, expiry display, "+$" budget bump (spend preserved),
  Revoke (kills the key on the proxy), ✉️ Send (prefilled mailto with
  the key + OPENAI_BASE_URL instructions), copy-key button
- lib/lms/litellm.ts: thin server-only layer over the proxy's
  /key/generate, /key/update, /key/delete, /key/info; section shows a
  setup note when LITELLM_PROXY_URL/LITELLM_MASTER_KEY are unset
- Student model gains apiKey/apiKeyBudget/apiKeyMintedAt/apiKeyExpiresAt
  (nullable — `yarn lms:push` after pulling)

Lessons — two new interactive islands (ephemeral, reset on reload):
- ```scenario (Scenario.tsx): workplace "what do you say?" role-play —
  a manager/PM/dev asks a nebulous question, student picks the reply
  they'd give, gets a graded verdict (best/ok/weak) with staff-engineer
  feedback, can reveal how the other replies land; 7 authored across
  the course: fine-tune-vs-RAG (d12), tool-calling for and against
  (d31 ×2), stale-docs index updates (d11), few-shot before fine-tune
  (d16), golden-set evals (d30), voice-vs-knowledge (d20)
- ```match (MatchPairs.tsx): tap-to-match pairs with lock-in on
  correct; chunking-strategy-per-content (d08) and
  retrieval-mode-per-query (d24)
- AUTHORING.md documents both formats with writing rules

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHrJeDcH8rmi1eabHbjEzD
- curriculum/day-00.md "Start Here — How to Win This Program", listed as
  Day 0 in the Week 1 index: why the video homework / Feynman Technique
  is the whole game, the mentor playbook (check-in as metronome, show
  what you built, ask for push-back, interview them, topics list, and
  the reach-out path if you don't have a mentor yet), the Slack "2%
  cheat code" on being present and sharing, momentum-beats-motivation
  habit guidance, the honest living-curriculum expectations (frequent
  updates, hiccups happen and how to flag them, text-first on purpose,
  weekly live sessions + mentors are the product, foundations over
  tools), a Day 0 checklist, and two AI prompts (success-plan interview,
  mentor-session rehearsal); day-01's getting-help section now points
  here instead of duplicating it
- New ```blanks island (FillBlanks.tsx): ___N___ markers in code become
  slots, option pills per blank, per-blank feedback with explanations on
  misses; placed on day-16 (temperature per job) and day-18 (the
  selector's zod schemas)
- New ```try-it island (TryIt.tsx + /api/lms/try): students run small
  real API calls with their own class key (from /admin key minting;
  localStorage only, relayed for exactly one call, models + token caps
  pinned server-side, Clerk-auth required): embedding-similarity
  (day-02), temperature 0.0-vs-1.4 (day-16), the selector with a strict
  JSON schema (day-18), and a live prompt-injection demo with a
  server-fixed poisoned document (day-34)
- AUTHORING.md documents both new islands; validator covers marker/blank
  parity, answers present in options, and known try-it kinds

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHrJeDcH8rmi1eabHbjEzD
- Curriculum alignment audit (scripted): all 26 Descript videos from the
  source curriculum's mapped lessons are on the correct days with none
  invented or dropped; the 2 interview-prep videos are on their pages;
  day structure matches DAY-SCHEDULE exactly. Site = source + supplements.
- New ungated bonus-lesson mechanism (slug prefix bonus-, README
  "## Bonus lessons" section): "Bonus — Optional Labs" list on /learn,
  ★ Optional lab page chrome, progress toggles work
- curriculum/bonus-bible-chunking.md: the medical-rag Bible chunking
  homework ported to this stack — download KJV from Project Gutenberg,
  provided loadVerses() parser inline (no repo/student-branch changes
  needed), watch fixed-size slicing fail, strategy tradeoff table +
  chunking visual, embed/upsert skeleton matching the upload-text route
  (512 dims, PINECONE_INDEX=bible-kjv), verification via searchDocuments
  ("who is my shepherd?" → Psalm 23 with reference), quiz, 2-3 min video
  posted to Slack, defend-my-strategy AI prompt; linked from day-08
- Fix: inline-code chip styling was applying inside dark <pre> blocks,
  making all block code in lessons render as unreadable light-on-light
  bars; chips now scope to :not(pre) > code only

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHrJeDcH8rmi1eabHbjEzD
Styling:
- Palette sweep across the LMS: indigo → blue-600 primary (now matching
  the visuals' #2563eb accent), AI-prompt blocks violet → cyan,
  interview-prep accents violet → sky — no more purple "AI colors"
- Cascade fix: the chat app's retro global styles (links, tables, form
  elements) now exclude the LMS via :not(.lms *) instead of being
  counter-reset inside .lms — the resets were beating Tailwind utilities
  and stripping button fills; the LMS is now fully utility-owned
- tailwind.config content now includes components/ (classes used only in
  components were silently missing from the build)

Easter eggs (all cosmetic, zero persistence):
- Konami code (↑↑↓↓←→←→BA) on /learn → "vector mode": the page becomes a
  drifting 2-D embedding space of course vocabulary for 12 seconds
- Styled console greeting for anyone who opens devtools, with a hint at
  the cheat code
- Confetti burst (site-blue palette) when marking a day done; a bigger
  one on day-42, whose header also carries a quiet Hitchhiker's nod
- Rest-day 🌴 rows sway on hover

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHrJeDcH8rmi1eabHbjEzD
… polish

Turn the branch into the deployed course site at ai.parsity.io.

Auth & access
- Clerk sign-in/up routes, middleware gating (/learn + /admin private,
  landing + auth pages public), production instance wiring
- Isolate LMS Prisma into its own schema; env-driven admin allowlist

Landing page
- Replace the RAG demo at / with a public animated landing (hero, embedding
  space, RAG pipeline, topics, mentorship/feedback/guest-speaker/interview-prep
  cards, humans-in-the-loop), reduced-motion safe; new SVG favicon

Deploy correctness
- Bundle the Prisma query engine into serverless funcs (outputFileTracing +
  binaryTargets) to fix PrismaClientInitializationError on Vercel
- Make ensureStudent idempotent/race-safe (P2002) and email-collision safe
- Lazy Pinecone client so builds don't need the key
- Bump Next.js to 15.5.x (middleware-bypass CVE)

Lessons
- Syntax-highlighted code (rehype-highlight), external links open in new tab,
  mark-done at top and bottom of each lesson

Curriculum
- Drop decorative emoji, remove unknown time estimates, add class-API-key
  note to day 5, reframe chunking around retrievability (not token limits),
  surface the Bible lab as optional-but-encouraged

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a dedicated "MCP in Production" lesson (three primitives, tool-surface
design, auth/permissions, threat modeling, transports) as a going-further
day, and make the curriculum freely reorderable.

- Day numbers are now COMPUTED from position in the README week index, not
  hardcoded. Adding, moving, or removing a lesson is a one-line edit; the
  filenames student progress is keyed on are never renamed. Week day-ranges
  compute too. Verified the existing numbering is unchanged (day-08 -> Day 8,
  rests at 7/14/21/28/35, etc.).
- New lessons may use semantic slugs instead of day-NN.
- The Day 42 easter egg is now slug-based, so it survives renumbering.
- Documented the add/move/remove workflow in AUTHORING.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Public "Would You Pass an AI Engineering Interview?" quiz at
  /ai-interview-quiz: 7 scenario questions drawn from real interviews,
  server-scored into 3 buckets, email (+ optional phone) capture, Calendly CTA.
  Leads stored in a new QuizLead table (lms schema); routes public in middleware.
- Admin eviction rewired from Clerk ban to delete. banUser is a paid-plan
  feature and errors on free, so "Remove" now deletes the account, revokes the
  student's proxy key, and drops their LMS row + progress (behind a confirm).
  Removes the now-dead ban/restore/"revoked" UI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Move the chunking corpus to 1536 dimensions, with a "Why 1536 here" section
  teaching the 512-vs-1536 tradeoff (dimension count is a knob, not a constant).
- Add a first-class "Retrieve from your index" step with a 1536-dim query path,
  turning the dims change into a lesson: the course's 512-dim searchDocuments
  throws against a 1536 index — query dims must match index dims.
- Optional generation stretch (retrieved verses -> cited answer).
- Name structure-aware chunking explicitly; add a retrieval reflection to the video.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BrianJenney added a commit that referenced this pull request Aug 3, 2026
The Inspector and claude mcp add commands both spawned 'npx tsx', which
this project doesn't depend on. When the npx fetch fails the Inspector
reports 'Command not found, transports removed' — an error about the
runner that reads like a broken server. Documented as failure #6.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants