Files
OpenSquawk/shared/learn/config.ts
itsrubberduck a956be42ed feat(classroom): show the full curriculum and raise the mastery bar
The overview rendered four anonymous module tiles and half a page of empty
space, which undersold 63 existing lessons. It now stacks four module
sections that list every lesson as a chip with status and variant dots, and
the header states the real scope: 4 modules, 63 lessons, 262 variations.

Mastery moves from a flat two-variation counter to per-module thresholds
(3/4/4/5) backed by persisted variant signatures — a hash over each roll's
expected answers. That triples the practice runs to full mastery without
authoring a single new lesson, and fixes a dedup hole where the same
variation counted twice after a page reload.

Existing progress is preserved: `done` is sticky, so raising the bar never
revokes a check mark someone already earned.

Also fixes `yarn test`, which passed quoted globs Node 20 does not expand —
it exited 0 without running a single test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 20:08:55 +02:00

72 lines
1.8 KiB
TypeScript

export interface LessonProgress {
best: number
done: boolean
assessmentVersion?: number
successfulVariants?: number
/**
* Signatures of the rolled scenarios already passed for this lesson, so the
* same variation cannot be counted twice — not even across page reloads.
*/
variantSignatures?: string[]
}
export type LearnProgress = Record<string, Record<string, LessonProgress>>
export interface LearnConfig {
tts: boolean
radioLevel: number
voice: string
audioChallenge: boolean
audioSpeed: number
}
export interface LearnState {
/** Legacy compatibility only. Classroom no longer awards or displays XP. */
xp: number
progress: LearnProgress
config: LearnConfig
/** Legacy compatibility only. All Classroom modules are now available. */
unlockedModules: string[]
}
export const CLASSROOM_ASSESSMENT_VERSION = 2
/**
* Clean variations required before a lesson counts as mastered. Short drills
* need less repetition than a full scenario, so the bar rises with module
* complexity. Every lesson rolls a fresh scenario, so the material is there.
*/
export const CLASSROOM_VARIANTS_BY_MODULE: Record<string, number> = {
'normalize': 3,
'arc': 4,
'decision-tree': 4,
'full-flight': 5,
}
export const CLASSROOM_VARIANTS_DEFAULT = 3
export function variantsForModule(moduleId: string): number {
return CLASSROOM_VARIANTS_BY_MODULE[moduleId] ?? CLASSROOM_VARIANTS_DEFAULT
}
export const LEARN_CONFIG_DEFAULTS: LearnConfig = {
tts: false,
radioLevel: 5,
voice: '',
audioChallenge: true,
audioSpeed: 0.85,
}
export function createDefaultLearnConfig(): LearnConfig {
return { ...LEARN_CONFIG_DEFAULTS }
}
export function createDefaultLearnState(): LearnState {
return {
xp: 0,
progress: {} as LearnProgress,
config: createDefaultLearnConfig(),
unlockedModules: [],
}
}