import { getChapters, getDefinitions, getFormulas, getNumericalPatterns, getQuestions, getVisualLessonFrames, type ChapterSummary, } from "@/lib/knowledge-base/sslc-physics"; import type { LessonSeedSourceRef } from "./physicsLessonSeeds"; import { getMagneticEffectEvidencePack } from "./source-overrides/magneticEffectEvidence"; export type PhysicsChapterSourcePack = { subject: "Physics"; chapterId: string; chapterTitle: string; sourceBook: string; pageRange?: string; sourceRefs: LessonSeedSourceRef[]; extractedSignals: { formulas: string[]; definitions: string[]; possibleQuestions: string[]; keyTerms: string[]; diagrams: string[]; numericalPatterns: string[]; }; coverageConfidence: "high" | "medium" | "low"; warnings: string[]; /** True when the extracted signals were repaired from a manual source-evidence override (Phase 6I). */ sourceRepaired?: boolean; }; /** * Manual source-evidence overrides (Phase 6I). These do NOT create lesson seeds * and do NOT make a chapter teachable — they only repair thin auto-extraction so * the source pack reflects the real (source-referenced) chapter scope. */ type SourceEvidenceOverride = { definitions: string[]; keyTerms: string[]; diagrams: string[]; possibleQuestions: string[]; }; function magneticEffectOverride(): SourceEvidenceOverride { const pack = getMagneticEffectEvidencePack(); return { definitions: pack.definitions.map((d) => `${d.term}: ${d.definition}`), keyTerms: pack.keyTerms, diagrams: pack.diagrams.map((d) => `${d.title}: ${d.visualDescription}`), possibleQuestions: pack.questions.map((q) => q.question), }; } function evidenceOverrideFor(chapterId: string): SourceEvidenceOverride | null { if (chapterId === "phy-p1-c4") return magneticEffectOverride(); return null; } const SOURCE_BOOK = "Kerala SCERT Physics, Standard X (2025)"; const MAX_SIGNAL_LENGTH = 180; const MAX_SIGNALS_PER_KIND = 20; function asRecord(value: unknown): Record { return value && typeof value === "object" ? (value as Record) : {}; } function readString(value: unknown): string { return typeof value === "string" ? value.trim() : ""; } function getChapterTitle(chapter: ChapterSummary): string { const maybeTitle = chapter as ChapterSummary & { title?: string }; return chapter.chapter_title?.trim() || maybeTitle.title?.trim() || chapter.chapter_id; } function compactText(value: string): string { return value.replace(/\s+/g, " ").trim().slice(0, MAX_SIGNAL_LENGTH); } function unique(values: string[]): string[] { return Array.from(new Set(values.map(compactText).filter(Boolean))).slice(0, MAX_SIGNALS_PER_KIND); } function firstText(record: Record, keys: string[]): string { for (const key of keys) { const value = readString(record[key]); if (value) return value; } return ""; } function sourcePageRange(chapter: ChapterSummary): string | undefined { const printedStart = Number(chapter.printed_start_page); const printedEnd = Number(chapter.printed_end_page); if (Number.isFinite(printedStart) && printedStart > 0) { return Number.isFinite(printedEnd) && printedEnd >= printedStart ? `${printedStart}-${printedEnd}` : String(printedStart); } const pdfStart = Number(chapter.pdf_start_page); const pdfEnd = Number(chapter.pdf_end_page); if (Number.isFinite(pdfStart) && pdfStart > 0) { return Number.isFinite(pdfEnd) && pdfEnd >= pdfStart ? `${pdfStart}-${pdfEnd}` : String(pdfStart); } return undefined; } function buildSourceRef(chapter: ChapterSummary): LessonSeedSourceRef { const pageRange = sourcePageRange(chapter); const chapterTitle = getChapterTitle(chapter); return { label: pageRange ? `Kerala SSLC Physics - ${chapterTitle} (pp. ${pageRange})` : `Kerala SSLC Physics - ${chapterTitle}`, sourceBook: SOURCE_BOOK, chapterTitle, pageRange, url: null, confidence: chapter.needs_review ? "needs_review" : "source_backed", }; } function formulaSignals(chapterId: string): string[] { return unique( (getFormulas(chapterId) ?? []).map((item) => { const record = asRecord(item); const formula = firstText(record, ["formula", "formula_used", "equation", "expression"]); const name = firstText(record, ["name", "title", "formula_name", "pattern_name"]); return formula && name ? `${name}: ${formula}` : formula || name; }), ); } function definitionSignals(chapterId: string): string[] { return unique( getDefinitions(chapterId).map((item) => { const term = item.term?.trim() ?? ""; const definition = item.definition?.trim() ?? ""; return definition ? `${term}: ${definition}` : term; }), ); } function questionSignals(chapterId: string): string[] { return unique(getQuestions(chapterId).map((item) => item.question)); } function termSignals(chapterId: string): string[] { const fromDefinitions = getDefinitions(chapterId).map((item) => item.term); const fromQuestions = getQuestions(chapterId).flatMap((item) => item.keywords ?? []); const fromFormulas = (getFormulas(chapterId) ?? []).map((item) => firstText(asRecord(item), ["name", "title"])); return unique([...fromDefinitions, ...fromQuestions, ...fromFormulas]); } function numericalSignals(chapterId: string): string[] { return unique( (getNumericalPatterns(chapterId) ?? []).map((item) => { const record = asRecord(item); const pattern = firstText(record, ["pattern_name", "title", "name"]); const formula = firstText(record, ["formula_used", "formula", "equation"]); return pattern && formula ? `${pattern}: ${formula}` : pattern || formula; }), ); } function diagramSignals(chapterId: string): string[] { return unique( (getVisualLessonFrames(chapterId) ?? []).flatMap((item) => { const record = asRecord(item); const title = firstText(record, ["title", "frame_title", "name"]); const idea = firstText(record, ["diagramDescription", "description", "teacherAction", "visual_idea"]); return [title, idea]; }), ); } function buildWarnings(signals: PhysicsChapterSourcePack["extractedSignals"], sourceRefs: LessonSeedSourceRef[]): string[] { const warnings: string[] = []; if (sourceRefs.length === 0) warnings.push("Missing chapter source reference."); if (signals.definitions.length === 0) warnings.push("No extracted definitions; concept missions need human curation."); if (signals.formulas.length === 0) warnings.push("No extracted formulas; formula/numerical missions need review."); if (signals.possibleQuestions.length < 10) warnings.push("Thin possible-question coverage."); if (signals.diagrams.length === 0) warnings.push("No extracted visual-board/diagram signal."); if (signals.numericalPatterns.length === 0) warnings.push("No extracted numerical pattern."); return warnings; } function confidenceFor(signals: PhysicsChapterSourcePack["extractedSignals"], sourceRefs: LessonSeedSourceRef[]): "high" | "medium" | "low" { if (sourceRefs.length === 0) return "low"; if (signals.definitions.length === 0 && signals.formulas.length === 0) return "low"; const signalScore = [ signals.definitions.length > 0, signals.formulas.length > 0, signals.possibleQuestions.length >= 10, signals.diagrams.length > 0, signals.numericalPatterns.length > 0, ].filter(Boolean).length; if (signalScore >= 4) return "high"; if (signalScore >= 2) return "medium"; return "low"; } function buildPack(chapter: ChapterSummary): PhysicsChapterSourcePack { const sourceRefs = [buildSourceRef(chapter)]; const extractedSignals: PhysicsChapterSourcePack["extractedSignals"] = { formulas: formulaSignals(chapter.chapter_id), definitions: definitionSignals(chapter.chapter_id), possibleQuestions: questionSignals(chapter.chapter_id), keyTerms: termSignals(chapter.chapter_id), diagrams: diagramSignals(chapter.chapter_id), numericalPatterns: numericalSignals(chapter.chapter_id), }; const override = evidenceOverrideFor(chapter.chapter_id); if (override) { extractedSignals.definitions = unique([...extractedSignals.definitions, ...override.definitions]); extractedSignals.keyTerms = unique([...extractedSignals.keyTerms, ...override.keyTerms]); extractedSignals.diagrams = unique([...extractedSignals.diagrams, ...override.diagrams]); extractedSignals.possibleQuestions = unique([...extractedSignals.possibleQuestions, ...override.possibleQuestions]); } const warnings = buildWarnings(extractedSignals, sourceRefs); if (override) { warnings.push( "Source manually repaired from the extracted textbook question bank and figure references (Phase 6I evidence override).", ); } return { subject: "Physics", chapterId: chapter.chapter_id, chapterTitle: getChapterTitle(chapter), sourceBook: SOURCE_BOOK, pageRange: sourceRefs[0]?.pageRange, sourceRefs, extractedSignals, coverageConfidence: confidenceFor(extractedSignals, sourceRefs), warnings, sourceRepaired: Boolean(override), }; } export function getPhysicsSourcePacks(): PhysicsChapterSourcePack[] { return getChapters().map(buildPack); } export function getPhysicsSourcePack(chapterId: string): PhysicsChapterSourcePack | null { return getPhysicsSourcePacks().find((pack) => pack.chapterId === chapterId) ?? null; }