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>
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
# 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 (T0–T2) + 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 (~62–68ch) 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.
|
||||
Reference in New Issue
Block a user