Files
Curio/CLAUDE.md
T
arnaudne 4e38b5a791 feat: initial commit — Curio mastery tutor
Full Next.js App Router application with:
- AI-graded lesson checkpoints (BKT/Elo mastery tracking)
- Auth.js v5: credentials, Google, GitHub, generic OIDC
- Anonymous-session-first with migrate-on-signin
- Admin panel: users, blueprints, reports, site settings
- Password reset + email verification (nodemailer/SMTP)
- Site config: require_auth + signups_enabled flags
- server-only guards on all DB/generation/verification modules
- PostgreSQL 16 + pgvector, Redis cache, Drizzle ORM
- 271 unit tests (Vitest), golden-eval harness, Playwright e2e stubs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 22:25:43 +02:00

102 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md — Curio
Repo context for Claude Code. Read this every session. Keep it current: if a convention changes, update this file in the same commit. Full product/architecture/design reference: `docs/curio-specification.md`.
## What Curio is (one paragraph)
Curio is a curiosity-led **mastery tutor**. A learner states intent → gets a Journey of bounded ~10-minute **Lessons** → each Lesson is short readable AI-generated explanation segments punctuated by **production checkpoints** (predict / explain-back / solve) → the AI **diagnoses** the learner's free-text response against pre-verified rubrics and a per-concept misconception library, gives targeted feedback, and gates advancement on demonstrated mastery. The moat is **diagnosis** (inferring *what* a learner misunderstands and *why*), not content generation.
## Decided stack — single language (TypeScript), do not substitute without asking
- **App:** Next.js (App Router) + TypeScript — RSC reading surface + Route Handlers (API) + native streaming/SSE
- **LLM:** Vercel AI SDK (provider-agnostic, streaming, schema-constrained structured output). Generator and grader configured to **different** models/providers.
- **Contracts:** Zod — shared across API I/O, LLM outputs, DB-derived types
- **DB:** PostgreSQL 16 + pgvector (single store, source of truth)
- **ORM/migrations:** Drizzle ORM + drizzle-kit
- **Cache/buffer:** Redis — a cache/buffer in *front* of Postgres, never load-bearing for availability
- **Background jobs:** Route Handlers + the `job` ledger table for the slice; **BullMQ** (Redis) when scale arrives — jobs spend money, so they must be **idempotent** with durable Postgres state
- **Styling/motion:** Tailwind + CSS custom properties (design tokens); motion via CSS transitions + **View Transitions API** (no animation library); honor `prefers-reduced-motion`
- **Observability:** Langfuse (or OpenTelemetry) for LLM call tracing
- **Rate limiting:** edge limiter (e.g. Upstash) on the cold-start generation endpoint
- **Tests:** Vitest (unit, LLM mocked) + golden-eval runner (real-model, CI) + Playwright (e2e)
- **Local infra:** docker-compose (postgres+pgvector, redis)
## Repo structure
```
curio/
CLAUDE.md
docker-compose.yml
docs/curio-specification.md
src/
app/ # Next.js App Router: reading surface + api/ route handlers
components/
lib/
llm/ # provider-agnostic client (Vercel AI SDK) + versioned prompt registry
generation/ # RAG-grounded content generation
verification/ # content cascade (T0T2) + response grading
memory/ # mastery (BKT/Elo) + spaced repetition
intent/ # intent normalization + blueprint lookup
db/ # drizzle schema, queries, migrations
schemas/ # zod contracts (shared)
styles/ # design tokens
jobs/ # BullMQ workers (deferred until P3)
tests/golden/ # frozen eval sets: content verification + response grading
e2e/ # playwright
```
## Architectural invariants — violating these is a bug
1. **All LLM access goes through `src/lib/llm/client.ts`.** Never call a provider/AI-SDK model directly elsewhere. Generator and grader pull their model from config; they must differ.
2. **Prompts live in the versioned registry** (`src/lib/llm/prompts/`), never inline. Each has an id + version.
3. **Contracts are Zod schemas** in `src/schemas/`, shared by the LLM layer, the API, and the UI. LLM structured output is validated against them.
4. **The serve path reads from the Redis buffer; jobs generate.** No synchronous content generation in request handlers except the documented cold-start gate. Postgres is the source of truth — a Redis outage degrades latency, not availability.
5. **Generated content stores provenance** (`source_chunk_ids`) so verification and grading are *grounded* against sources, not free-judged.
6. **Grading is constrained.** The grader evaluates against a pre-verified rubric + reference answer + misconception library. Verdict ∈ {`mastered`,`partial`,`misconception`,`uncertain`}. It **must** support `uncertain` (abstain → reveal). **Bias against false-fail** — telling a correct learner they're wrong is the worst error.
7. **Verification is a cascade.** T0 structural (free) → T1 cheap-model → T2 strong-model (adversarial, at blueprint promotion). Expensive checks amortized once per blueprint, never per serve.
8. **Paid background jobs are idempotent** (idempotency key + durable `job` state). A retry must never regenerate-and-rebill.
9. **Per-session token budget is enforced in code.** The ~10-min Lesson bound is a real cap.
10. **Content is versioned** by `(intent_key, content_version, model_version)`; invalidation is lazy.
## Design invariants (see spec §7)
- **Thesis:** a calm, editorial reading surface that comes alive only when the learner responds. Not gamified, not a dashboard, not a chat-bubble app.
- **Signature:** diagnosis-as-**marginalia** — feedback threaded against the learner's own words, pointing at the `evidence_span` that tripped them. Spend the design's boldness here; keep everything else quiet.
- **Motion** is meaning, not decoration: reserved for the diagnosis moment (a brief "considering" beat → feedback eases in). CSS/View Transitions only.
- **Reading is the product:** humanist reading serif, generous measure (~6268ch) and leading; display face used with restraint; tokens centralized in `src/styles/`.
- **Quality floor (non-negotiable):** responsive to mobile, visible keyboard focus, reduced-motion respected, WCAG-AA contrast (including every diagnosis-state color).
- **Before building any UI, run the frontend-design two-pass process** (plan tokens → critique for genericness → build). Avoid AI-default looks (cream+serif+terracotta, near-black+acid accent, broadsheet hairlines).
- Copy is design material: warm, specific, plain, on the learner's side; never gamified or condescending.
## Commands
```bash
docker compose up -d # postgres+pgvector, redis
pnpm install # or npm
pnpm db:generate && pnpm db:migrate
pnpm dev # Next.js
pnpm test # vitest (unit; LLM mocked)
pnpm test:golden # real-model eval harness (CI-gated)
pnpm test:e2e # playwright
pnpm lint && pnpm format # eslint + prettier
```
## Testing rules
- Write/extend tests **with** each change; run the suite before declaring a task done.
- **Mock the LLM in unit tests** (deterministic fixtures). Real-model calls only in the golden-eval suite.
- Golden evals (`tests/golden/`) are the bar for the moat: response-grading accuracy (track **false-fail rate**) + content-verification accuracy. Don't regress them; add cases on new failure modes.
- Migrations: never edit an applied drizzle migration; add a new one.
## Out of scope — do NOT build unless explicitly asked
Gamification (XP/badges/leaderboards), infinite feed / social, multi-provider fallback orchestration, knowledge graph, voice, PDF ingestion, accounts (MVP is anonymous-session-first). If a task seems to require one of these, stop and ask.
## Working agreement
- **Plan before coding** for anything non-trivial; surface architecture- or design-affecting choices before implementing.
- Small, focused commits per task. One ticket = one logical change.
- Keep Route Handlers thin; logic lives in `src/lib/`.
- Run the frontend-design planning pass before UI tickets.
- If you change a convention here, update this file in the same commit.
- When unsure between two reasonable designs, ask rather than guess — especially on the grading/verification path and the diagnosis UI.