curriculum: runtime JSON loading, retire CurriculumRegistry.swift
Created by: jedmund
Summary
Initial migration: move curriculum from a 1339-line Swift literal to runtime JSON loading so authoring doesn't require Swift edits. Then, per feedback that the architecture needed to scale to ~600 lessons, add three follow-ups in the same PR:
Migration (commit 1):
-
lessons/**/*.jsonis now the runtime source of truth. Newlessons/index.json+ per-tracklessons/{track}/track.jsoncarry the track/module metadata that used to live in Swift. The 30 per-lesson files are unchanged. -
ContentLoader.loadCurriculum()walks the tree andfatalErrors on any missing/malformed file. -
CurriculumRegistry.swiftdeleted.PreviewFixtures.swiftbacks the three #Preview blocks.
Lazy lesson loading (commit 2):
- New
LessonSummary(id, track, module, position, title, tagline, ref path).Module.lessons: [LessonSummary]. -
loadCurriculum()now reads only lesson metadata at launch (not full payloads); the fullLessonloads on demand viaContentLoader.loadLesson(_:)when a lesson page opens. Cuts in-memory curriculum size ~10x at full scope. -
track.jsondrops itslessons[]arrays; lesson files are auto-discovered by{module}-{position}.jsonfilename pattern. Removes the "forgot to add to track.json" class of bug entirely. -
LessonScreenis a thin wrapper that loads and hands the fullLessonto an innerLessonScreenContent.
Content fix (commit 3):
- Six lessons (
music-theory/2-1..2-6) were usingdevice_categories: ["linnstrument"]where the established pattern isdevices: ["linnstrument-200"], device_categories: []. Caught by the validator on its first run, fixed in a standalone commit.
Validator + pre-commit hook + CI (commit 4):
-
scripts/validate_curriculum.pywalks the full content graph. Checks every path ref resolves; everytoolvalue matches aToolTyperaw value; everycheck.typematches aCheckTyperaw value; everydevice_categoriesvalue is in the known vocab;track/module/positionmatch directory + filename; no orphan lesson files exist. -
.githooks/pre-commitruns the validator before commit (enable withgit config core.hooksPath .githooks); skips gracefully ifuvisn't installed. -
.github/workflows/validate-curriculum.ymlruns the validator on every PR and main push as the hard gate.
Test plan
-
Swift build passes (iPad Pro sim, Metal Toolchain installed). -
uv run --project scripts scripts/validate_curriculum.pypasses locally. -
Walk through the home screen and into a lesson in each track. Confirm all content renders identically to pre-change. -
Edit a lesson title in JSON, rebuild, confirm the change appears with no Swift changes. -
Break a config_reftemporarily; confirm the validator reports it with file:field location and exits nonzero; restore. -
Install the pre-commit hook ( git config core.hooksPath .githooks), attempt a bad commit, confirm it's blocked. -
Open a draft PR with a broken lesson file; confirm the GitHub Action fails.