Download src/lib/teaching/magneticEffectCoverageGate.ts from asnannp/DocDoeAI: direct link, hf CLI and curl.
- Browser
- Download file 6.91 kB
-
https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/lib/teaching/magneticEffectCoverageGate.ts
- Command line
-
hf download hf://spaces/asnannp/DocDoeAI/src/lib/teaching/magneticEffectCoverageGate.ts
-
curl -L -o magneticEffectCoverageGate.ts https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/lib/teaching/magneticEffectCoverageGate.ts
6.91 kB
| /** | |
| * Phase 6I — Magnetic Effect coverage-readiness gate. | |
| * | |
| * Evaluates the reconstructed Magnetic Effect evidence pack and decides whether | |
| * Phase 6J (final lesson-seed authoring) can safely start. This gate NEVER sets | |
| * canTeach, never registers seeds, and never claims curated PYQ. It only reports | |
| * source-readiness. | |
| */ | |
| import { | |
| getMagneticEffectEvidencePack, | |
| type EvidenceConfidence, | |
| type MagneticChapterEvidencePack, | |
| type MagneticEvidenceRef, | |
| } from "./source-overrides/magneticEffectEvidence"; | |
| export type MagneticCoverageGateResult = { | |
| chapterId: "phy-p1-c4"; | |
| status: "blocked" | "draftable" | "curation_ready" | "needs_manual_source_review"; | |
| score: number; | |
| requiredTopics: Array<{ | |
| topicId: string; | |
| topicTitle: string; | |
| status: "covered" | "partial" | "missing"; | |
| evidenceCount: number; | |
| warnings: string[]; | |
| }>; | |
| missingCriticalTopics: string[]; | |
| sourceQualityWarnings: string[]; | |
| canStartPhase6J: boolean; | |
| }; | |
| const FAKE_PREDICTION_PATTERNS = [ | |
| /guaranteed question/i, | |
| /100\s*%\s*com(e|ing)/i, | |
| /sure question/i, | |
| /surely (will )?be asked/i, | |
| /definitely (will )?come/i, | |
| /confirm(ed)? question/i, | |
| /exact prediction/i, | |
| ]; | |
| // The topics a safe Phase 6J seed set must be able to teach from source. | |
| const REQUIRED_TOPIC_IDS = [ | |
| "magnetic-field-around-conductor", | |
| "direction-of-field-right-hand-rule", | |
| "solenoid", | |
| "electromagnet", | |
| "force-on-current-carrying-conductor", | |
| "electric-motor", | |
| ] as const; | |
| function isSourceBacked(confidence: EvidenceConfidence): boolean { | |
| return confidence === "high" || confidence === "medium"; | |
| } | |
| function allRefs(pack: MagneticChapterEvidencePack): MagneticEvidenceRef[] { | |
| return [ | |
| ...pack.definitions.flatMap((d) => d.sourceRefs), | |
| ...pack.formulasAndRules.flatMap((f) => f.sourceRefs), | |
| ...pack.diagrams.flatMap((d) => d.sourceRefs), | |
| ...pack.questions.flatMap((q) => q.sourceRefs), | |
| ...pack.activities.flatMap((a) => a.sourceRefs), | |
| ]; | |
| } | |
| export function evaluateMagneticEffectCoverageGate( | |
| pack: MagneticChapterEvidencePack = getMagneticEffectEvidencePack(), | |
| ): MagneticCoverageGateResult { | |
| const sourceQualityWarnings: string[] = []; | |
| const sourceBackedDefinitions = pack.definitions.filter((d) => isSourceBacked(d.confidence)); | |
| const diagramAndActivityCount = pack.diagrams.length + pack.activities.length; | |
| const sourceBackedQuestions = pack.questions.filter((q) => isSourceBacked(q.confidence)); | |
| // --- Hard requirement checks ------------------------------------------------- | |
| const checks = { | |
| enoughDefinitions: sourceBackedDefinitions.length >= 5, | |
| enoughDiagrams: diagramAndActivityCount >= 3, | |
| enoughQuestions: sourceBackedQuestions.length >= 5, | |
| allSourceRefsPresent: allRefs(pack).length > 0 && | |
| [...pack.definitions, ...pack.formulasAndRules, ...pack.diagrams, ...pack.questions, ...pack.activities].every( | |
| (item) => item.sourceRefs.length > 0, | |
| ), | |
| noEmptySourceMeta: allRefs(pack).every((r) => r.sourceBook.trim().length > 0 && r.chapterTitle.trim().length > 0), | |
| pageRangeKnown: Boolean(pack.pageRange && pack.pageRange.trim().length > 0), | |
| }; | |
| if (!checks.enoughDefinitions) sourceQualityWarnings.push("Fewer than 5 source-backed definitions/key concepts."); | |
| if (!checks.enoughDiagrams) sourceQualityWarnings.push("Fewer than 3 diagram/activity evidence items."); | |
| if (!checks.enoughQuestions) sourceQualityWarnings.push("Fewer than 5 source-backed question evidence items."); | |
| if (!checks.allSourceRefsPresent) sourceQualityWarnings.push("Some evidence item is missing sourceRefs."); | |
| if (!checks.noEmptySourceMeta) sourceQualityWarnings.push("An evidence sourceRef has an empty sourceBook/chapterTitle."); | |
| if (!checks.pageRangeKnown) sourceQualityWarnings.push("Chapter page range is unknown."); | |
| // --- Honesty guards ---------------------------------------------------------- | |
| const packText = JSON.stringify(pack); | |
| for (const pattern of FAKE_PREDICTION_PATTERNS) { | |
| if (pattern.test(packText)) { | |
| sourceQualityWarnings.push(`Evidence contains fake-prediction language (matched ${pattern}).`); | |
| } | |
| } | |
| // --- Required-topic coverage ------------------------------------------------- | |
| const requiredTopics = REQUIRED_TOPIC_IDS.map((topicId) => { | |
| const topic = pack.coverageMap.find((t) => t.topicId === topicId); | |
| if (!topic) { | |
| return { topicId, topicTitle: topicId, status: "missing" as const, evidenceCount: 0, warnings: ["Topic not present in the coverage map."] }; | |
| } | |
| const status: "covered" | "partial" | "missing" = | |
| topic.status === "source_backed" ? "covered" : topic.status === "not_found" ? "missing" : "partial"; | |
| return { | |
| topicId: topic.topicId, | |
| topicTitle: topic.topicTitle, | |
| status, | |
| evidenceCount: topic.evidenceIds.length, | |
| warnings: topic.missingPieces, | |
| }; | |
| }); | |
| const missingCriticalTopics = requiredTopics.filter((t) => t.status === "missing").map((t) => t.topicId); | |
| const partialCriticalTopics = requiredTopics.filter((t) => t.status === "partial").map((t) => t.topicId); | |
| // --- Score ------------------------------------------------------------------- | |
| const hardChecksPassed = Object.values(checks).filter(Boolean).length; | |
| const coveredCritical = requiredTopics.filter((t) => t.status === "covered").length; | |
| const score = Math.round( | |
| ((hardChecksPassed / Object.keys(checks).length) * 0.5 + (coveredCritical / REQUIRED_TOPIC_IDS.length) * 0.5) * 100, | |
| ); | |
| // --- Status decision --------------------------------------------------------- | |
| const allHardChecksPass = hardChecksPassed === Object.keys(checks).length; | |
| const noFakeLanguage = !sourceQualityWarnings.some((w) => w.includes("fake-prediction")); | |
| const noMissingCritical = missingCriticalTopics.length === 0; | |
| let status: MagneticCoverageGateResult["status"]; | |
| let canStartPhase6J: boolean; | |
| if (!noFakeLanguage || !checks.allSourceRefsPresent || !checks.noEmptySourceMeta) { | |
| status = "needs_manual_source_review"; | |
| canStartPhase6J = false; | |
| } else if (allHardChecksPass && noMissingCritical) { | |
| status = "curation_ready"; | |
| canStartPhase6J = true; | |
| } else if (checks.enoughDefinitions && checks.enoughQuestions && noMissingCritical) { | |
| // Enough core evidence to draft, even if some quality checks are soft. | |
| status = "draftable"; | |
| canStartPhase6J = true; | |
| } else if (missingCriticalTopics.length > 0 || (!checks.enoughDefinitions && !checks.enoughQuestions)) { | |
| status = "blocked"; | |
| canStartPhase6J = false; | |
| } else { | |
| status = "draftable"; | |
| canStartPhase6J = false; | |
| } | |
| if (partialCriticalTopics.length > 0) { | |
| sourceQualityWarnings.push(`Partial source for: ${partialCriticalTopics.join(", ")}.`); | |
| } | |
| return { | |
| chapterId: "phy-p1-c4", | |
| status, | |
| score, | |
| requiredTopics, | |
| missingCriticalTopics, | |
| sourceQualityWarnings, | |
| canStartPhase6J, | |
| }; | |
| } | |