feat: full feature buildout — streaming, i18n, mastery map, admin, jobs
Progressive lesson streaming via onSegment callback (fixes SSE for non-English users — locale was shadowed in lesson-reader useEffect). Adds: BullMQ workers, Redis stream buffer, token budget enforcement, Langfuse tracing, golden-eval runner, Playwright e2e scaffolding, lesson depth/locale/preferences schema, mastery map UI, admin panel (blueprints/users/reports/quality/misconceptions), image queries, source citations, view transitions, reading animations, i18n (next-intl), PDF export, surprise endpoint, and 402 passing unit tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
ALTER TABLE "user" ADD COLUMN "locale" varchar(10) DEFAULT 'en' NOT NULL;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "lesson" ADD COLUMN "locale" varchar(10) DEFAULT 'en' NOT NULL;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "lesson" ADD COLUMN "locale" varchar(10) DEFAULT 'en' NOT NULL;
|
||||
@@ -0,0 +1,13 @@
|
||||
CREATE TABLE "invite" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"token" text NOT NULL,
|
||||
"email" varchar(255),
|
||||
"role" "user_role" DEFAULT 'learner' NOT NULL,
|
||||
"invited_by" uuid NOT NULL,
|
||||
"expires" timestamp NOT NULL,
|
||||
"accepted_at" timestamp,
|
||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "invite_token_unique" UNIQUE("token")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "invite" ADD CONSTRAINT "invite_invited_by_user_id_fk" FOREIGN KEY ("invited_by") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "lesson" ADD COLUMN "depth" varchar(20) DEFAULT 'standard' NOT NULL;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "lesson" ADD COLUMN "depth" varchar(20) DEFAULT 'standard' NOT NULL;
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE "user" ADD COLUMN "preferences" jsonb NOT NULL DEFAULT '{}';--> statement-breakpoint
|
||||
ALTER TABLE "lesson" ADD COLUMN "age_group" varchar(10) NOT NULL DEFAULT 'adult';
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE "lesson" ADD COLUMN "age_group" varchar(10) DEFAULT 'adult' NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE "user" ADD COLUMN "preferences" jsonb DEFAULT '{}'::jsonb NOT NULL;
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE "blueprint" ADD COLUMN "prompt_version" integer DEFAULT 0 NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE "blueprint" ADD COLUMN "stale_at" timestamp;
|
||||
@@ -0,0 +1,12 @@
|
||||
CREATE TABLE "eval_run" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"suite" varchar(50) NOT NULL,
|
||||
"false_fail_rate" real,
|
||||
"accuracy" real,
|
||||
"total" integer NOT NULL,
|
||||
"passed" integer DEFAULT 0 NOT NULL,
|
||||
"model_version" text NOT NULL,
|
||||
"prompt_version" integer DEFAULT 0 NOT NULL,
|
||||
"git_sha" varchar(40),
|
||||
"created_at" timestamp DEFAULT now() NOT NULL
|
||||
);
|
||||
@@ -0,0 +1,30 @@
|
||||
-- Theme taxonomy table (fixed, seeded once)
|
||||
CREATE TABLE "theme" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
"slug" text NOT NULL UNIQUE,
|
||||
"label_en" text NOT NULL,
|
||||
"label_fr" text NOT NULL,
|
||||
"emoji" text NOT NULL
|
||||
);
|
||||
|
||||
-- Seed fixed taxonomy
|
||||
INSERT INTO "theme" ("slug", "label_en", "label_fr", "emoji") VALUES
|
||||
('sciences-naturelles', 'Natural sciences', 'Sciences naturelles', '🔬'),
|
||||
('mathematiques', 'Mathematics', 'Mathématiques', '∑'),
|
||||
('histoire', 'History', 'Histoire', '📜'),
|
||||
('geographie', 'Geography', 'Géographie', '🌍'),
|
||||
('langues-litterature', 'Languages & literature', 'Langues & littérature', '📖'),
|
||||
('arts-culture', 'Arts & culture', 'Arts & culture', '🎨'),
|
||||
('technologie-informatique', 'Technology', 'Technologie', '💻'),
|
||||
('philosophie-ethique', 'Philosophy & ethics', 'Philosophie & éthique', '💭'),
|
||||
('economie-societe', 'Economics & society', 'Économie & société', '📊'),
|
||||
('sante-medecine', 'Health & medicine', 'Santé & médecine', '🏥'),
|
||||
('sport-activite', 'Sport & activity', 'Sport & activité', '⚡'),
|
||||
('autre', 'Other', 'Autre', '✦');
|
||||
|
||||
-- Blueprint ↔ theme join
|
||||
CREATE TABLE "blueprint_theme" (
|
||||
"blueprint_id" uuid NOT NULL REFERENCES "blueprint"("id") ON DELETE CASCADE,
|
||||
"theme_id" uuid NOT NULL REFERENCES "theme"("id") ON DELETE CASCADE,
|
||||
PRIMARY KEY ("blueprint_id", "theme_id")
|
||||
);
|
||||
@@ -0,0 +1,17 @@
|
||||
CREATE TABLE "blueprint_theme" (
|
||||
"blueprint_id" uuid NOT NULL,
|
||||
"theme_id" uuid NOT NULL,
|
||||
CONSTRAINT "blueprint_theme_blueprint_id_theme_id_pk" PRIMARY KEY("blueprint_id","theme_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "theme" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"slug" text NOT NULL,
|
||||
"label_en" text NOT NULL,
|
||||
"label_fr" text NOT NULL,
|
||||
"emoji" text NOT NULL,
|
||||
CONSTRAINT "theme_slug_unique" UNIQUE("slug")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "blueprint_theme" ADD CONSTRAINT "blueprint_theme_blueprint_id_blueprint_id_fk" FOREIGN KEY ("blueprint_id") REFERENCES "public"."blueprint"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "blueprint_theme" ADD CONSTRAINT "blueprint_theme_theme_id_theme_id_fk" FOREIGN KEY ("theme_id") REFERENCES "public"."theme"("id") ON DELETE cascade ON UPDATE no action;
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -43,6 +43,90 @@
|
||||
"when": 1782070177922,
|
||||
"tag": "0005_site_email",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 6,
|
||||
"version": "7",
|
||||
"when": 1782074310619,
|
||||
"tag": "0006_slim_ultragirl",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"version": "7",
|
||||
"when": 1750550400000,
|
||||
"tag": "0007_lesson_locale",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"version": "7",
|
||||
"when": 1782133019007,
|
||||
"tag": "0008_noisy_cloak",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 9,
|
||||
"version": "7",
|
||||
"when": 1782133583085,
|
||||
"tag": "0009_unique_wallflower",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 10,
|
||||
"version": "7",
|
||||
"when": 1782200000000,
|
||||
"tag": "0010_lesson_depth",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 11,
|
||||
"version": "7",
|
||||
"when": 1782138081002,
|
||||
"tag": "0011_smooth_thanos",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 12,
|
||||
"version": "7",
|
||||
"when": 1782500000000,
|
||||
"tag": "0012_user_preferences",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 13,
|
||||
"version": "7",
|
||||
"when": 1782310455008,
|
||||
"tag": "0013_illegal_random",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 14,
|
||||
"version": "7",
|
||||
"when": 1782312574334,
|
||||
"tag": "0014_faulty_namor",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 15,
|
||||
"version": "7",
|
||||
"when": 1782313119046,
|
||||
"tag": "0015_mighty_prowler",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 16,
|
||||
"version": "7",
|
||||
"when": 1782400000000,
|
||||
"tag": "0016_theme_map",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 17,
|
||||
"version": "7",
|
||||
"when": 1782337567021,
|
||||
"tag": "0017_married_betty_brant",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user