@thanh01.pmt/curriculum-kit 1.0.13 → 1.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -12,7 +12,6 @@ import { createClient } from '@supabase/supabase-js';
12
12
  import { fileURLToPath } from 'url';
13
13
  import { jsonrepair } from 'jsonrepair';
14
14
  import { Octokit } from '@octokit/rest';
15
- import { defineHook, getStepMetadata, RetryableError, sleep } from 'workflow';
16
15
 
17
16
  var __defProp = Object.defineProperty;
18
17
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -14661,1159 +14660,233 @@ template_contract: "artifact-template-v1"
14661
14660
  timestamp: dateStr
14662
14661
  };
14663
14662
  }
14664
- async function withRateLimitBackoff(options) {
14665
- let attempt = 1;
14666
- try {
14667
- const meta = getStepMetadata();
14668
- if (meta && typeof meta.attempt === "number") {
14669
- attempt = meta.attempt;
14670
- }
14671
- } catch {
14672
- }
14673
- try {
14674
- return await options.fn();
14675
- } catch (error) {
14676
- const errorMessage = error?.message || String(error);
14677
- const status = error?.status || error?.statusCode || error?.response?.status;
14678
- const isRateLimited = status === 429 || errorMessage.includes("429") || errorMessage.toLowerCase().includes("rate limit") || errorMessage.toLowerCase().includes("resource exhausted") || errorMessage.toLowerCase().includes("quota exceeded");
14679
- const isTransientError = status === 500 || status === 502 || status === 503 || status === 504 || errorMessage.includes("fetch failed") || errorMessage.includes("ECONNRESET") || errorMessage.includes("ETIMEDOUT") || errorMessage.includes("socket hang up");
14680
- if (isRateLimited || isTransientError) {
14681
- const retryAfterSeconds = Math.min(120, Math.pow(2, attempt) * 2);
14682
- const reason = isRateLimited ? "Rate limit exceeded (429)" : `Transient server error (${status || "network"})`;
14683
- throw new RetryableError(`[${options.stepName}] ${reason}. Retrying attempt ${attempt + 1}...`, {
14684
- retryAfter: `${retryAfterSeconds}s`
14685
- });
14686
- }
14687
- throw error;
14688
- }
14689
- }
14690
14663
 
14691
- // src/workflow/steps/lessonSteps.ts
14692
- async function generateMasterLessonStep(input) {
14693
- "use step";
14694
- console.log(` \u23F3 [Step: Master Lesson] Generating 5E Master Lesson Plan for "${input.milestone.name}"...`);
14695
- const t0 = Date.now();
14696
- const res = await withRateLimitBackoff({
14697
- stepName: `generate-master-lesson-${input.milestone.id || "L01"}`,
14698
- fn: async () => {
14699
- return generateLessonMasterFlow({
14700
- milestone: input.milestone,
14701
- language: input.language,
14702
- topic: input.topic,
14703
- targetAudience: input.targetAudience,
14704
- contextContinuity: input.contextContinuity,
14705
- standardsContext: input.standardsContext,
14706
- modelOptions: input.modelOptions
14664
+ // src/index.ts
14665
+ init_errors();
14666
+
14667
+ // src/evaluators/deterministicStructuralLinter.ts
14668
+ var DeterministicStructuralLinter = class {
14669
+ /**
14670
+ * Validates structural invariants across a complete lesson bundle.
14671
+ */
14672
+ static lintBundle(input) {
14673
+ const { lesson, quiz, activity, slides, codeLab } = input;
14674
+ const findings = [];
14675
+ const strengths = [];
14676
+ let score = 100;
14677
+ const baseLessonId = lesson.lessonId;
14678
+ const baseLanguage = lesson.language;
14679
+ if (!lesson.title || lesson.title.trim().length === 0) {
14680
+ score -= 20;
14681
+ findings.push({
14682
+ id: "struct_missing_lesson_title",
14683
+ dimension: "CROSS_ARTIFACT_ZERO_DRIFT",
14684
+ severity: "CRITICAL",
14685
+ title: "Missing Lesson Title",
14686
+ description: "Lesson plan has an empty or whitespace title.",
14687
+ remediationAdvice: "Provide a non-empty lesson title."
14707
14688
  });
14708
14689
  }
14709
- });
14710
- console.log(` \u2705 [Step: Master Lesson] Done in ${((Date.now() - t0) / 1e3).toFixed(1)}s (Title: "${res.title}")`);
14711
- return res;
14712
- }
14713
- async function judgeMasterLessonStep(input) {
14714
- "use step";
14715
- console.log(` \u2696\uFE0F [Step: Quality Judge] Auditing Master Lesson pedagogical quality & language adherence...`);
14716
- const t0 = Date.now();
14717
- const res = await withRateLimitBackoff({
14718
- stepName: `judge-master-lesson-${input.lessonId}`,
14719
- fn: async () => {
14720
- return auditCurriculumQualityFlow({
14721
- targetArtifactType: "LESSON",
14722
- lessonId: input.lessonId,
14723
- expectedLanguage: input.language,
14724
- targetObjectives: input.targetObjectives,
14725
- generatedContentJson: JSON.stringify(input.lesson, null, 2),
14726
- standardStatements: input.standardStatements,
14727
- modelOptions: input.modelOptions
14690
+ if (!lesson.learningObjectives || lesson.learningObjectives.length === 0) {
14691
+ score -= 25;
14692
+ findings.push({
14693
+ id: "struct_empty_learning_objectives",
14694
+ dimension: "CONSTRUCTIVE_ALIGNMENT",
14695
+ severity: "CRITICAL",
14696
+ title: "Empty Learning Objectives Array",
14697
+ description: "Lesson plan contains 0 learning objectives.",
14698
+ remediationAdvice: "Declare at least 2 structured Learning Objectives in the schema."
14728
14699
  });
14729
14700
  }
14730
- });
14731
- console.log(` \u2705 [Step: Quality Judge] Done in ${((Date.now() - t0) / 1e3).toFixed(1)}s (Verdict: ${res.overallVerdict}, Score: ${res.totalScore}/100)`);
14732
- return res;
14733
- }
14734
- async function repairMasterLessonStep(input) {
14735
- "use step";
14736
- console.log(` \u{1F6E0}\uFE0F [Step: Auto-Repair] Repairing Master Lesson based on Judge feedback...`);
14737
- const repairFeedback = `
14738
- PREVIOUS AUDIT VERDICT: ${input.auditReport.overallVerdict} (Score: ${input.auditReport.totalScore}/100)
14739
- Detected Issues:
14740
- ${input.auditReport.criteria.filter((c) => !c.passed).map((c) => `- [${c.name}] ${c.feedback}`).join("\n")}
14741
- Actionable Repairs:
14742
- ${input.auditReport.actionableRepairPrompts.map((p) => `* ${p}`).join("\n")}
14743
- Language Adherence Required: "${input.language}"
14744
- `.trim();
14745
- return withRateLimitBackoff({
14746
- stepName: `repair-master-lesson-${input.milestone.id || "L01"}`,
14747
- fn: async () => {
14748
- return generateLessonMasterFlow({
14749
- milestone: input.milestone,
14750
- language: input.language,
14751
- topic: input.topic,
14752
- targetAudience: input.targetAudience,
14753
- contextContinuity: repairFeedback,
14754
- standardsContext: input.standardsContext,
14755
- modelOptions: input.modelOptions
14701
+ if (!lesson.sections || lesson.sections.length === 0) {
14702
+ score -= 25;
14703
+ findings.push({
14704
+ id: "struct_empty_lesson_sections",
14705
+ dimension: "5E_INSTRUCTIONAL_FIDELITY",
14706
+ severity: "CRITICAL",
14707
+ title: "Empty Lesson Sections Array",
14708
+ description: "Lesson plan contains no instructional sections.",
14709
+ remediationAdvice: "Provide structured lesson flow sections."
14756
14710
  });
14757
14711
  }
14758
- });
14759
- }
14760
-
14761
- // src/workflow/steps/satelliteSteps.ts
14762
- async function generateActivityStep(options) {
14763
- "use step";
14764
- console.log(` \u23F3 [Step: Activity Lab] Generating hands-on ACT.md...`);
14765
- const t0 = Date.now();
14766
- const res = await withRateLimitBackoff({
14767
- stepName: `generate-act-${options.lesson.lessonId}`,
14768
- fn: async () => generateActivityFlow(options)
14769
- });
14770
- console.log(` \u2705 [Step: Activity Lab] Done in ${((Date.now() - t0) / 1e3).toFixed(1)}s (Title: "${res.title}")`);
14771
- return res;
14772
- }
14773
- async function generateCodeLabStep(options) {
14774
- "use step";
14775
- console.log(` \u23F3 [Step: Code Lab] Generating starter & solution code LAB.md...`);
14776
- const t0 = Date.now();
14777
- const res = await withRateLimitBackoff({
14778
- stepName: `generate-lab-${options.lesson.lessonId}`,
14779
- fn: async () => generateCodeLabFlow(options)
14780
- });
14781
- console.log(` \u2705 [Step: Code Lab] Done in ${((Date.now() - t0) / 1e3).toFixed(1)}s (Title: "${res.title}")`);
14782
- return res;
14783
- }
14784
- async function generateSelfLabStep(options) {
14785
- "use step";
14786
- console.log(` \u23F3 [Step: Self-Lab] Generating SELF_LAB.md with Progressive Hints...`);
14787
- const t0 = Date.now();
14788
- const res = await withRateLimitBackoff({
14789
- stepName: `generate-self-lab-${options.milestone.id || "L01"}`,
14790
- fn: async () => generateSelfLabFlow(options)
14791
- });
14792
- console.log(` \u2705 [Step: Self-Lab] Done in ${((Date.now() - t0) / 1e3).toFixed(1)}s (Challenges: 3 tiers Bronze/Silver/Gold)`);
14793
- return res;
14794
- }
14795
- async function generateDiagnosticQuizStep(options) {
14796
- "use step";
14797
- console.log(` \u23F3 [Step: Diagnostic Quiz] Generating QUIZ.json Bloom Assessment...`);
14798
- const t0 = Date.now();
14799
- const res = await withRateLimitBackoff({
14800
- stepName: `generate-quiz-${options.milestone.id || "L01"}`,
14801
- fn: async () => generateDiagnosticQuizFlow(options)
14802
- });
14803
- console.log(` \u2705 [Step: Diagnostic Quiz] Done in ${((Date.now() - t0) / 1e3).toFixed(1)}s (Questions: ${res.questions.length})`);
14804
- return res;
14805
- }
14806
- async function generateSlidesStep(options) {
14807
- "use step";
14808
- console.log(` \u23F3 [Step: Slides] Generating Marp presentation SLIDE.md...`);
14809
- const t0 = Date.now();
14810
- const res = await withRateLimitBackoff({
14811
- stepName: `generate-slides-${options.lesson.lessonId}`,
14812
- fn: async () => generateSlidesFlow(options)
14813
- });
14814
- console.log(` \u2705 [Step: Slides] Done in ${((Date.now() - t0) / 1e3).toFixed(1)}s (Slides: ${res.slides.length})`);
14815
- return res;
14816
- }
14817
- async function generateHandoutStep(options) {
14818
- "use step";
14819
- console.log(` \u23F3 [Step: Handout] Generating 5-Second Rule HANDOUT.md...`);
14820
- const t0 = Date.now();
14821
- const res = await withRateLimitBackoff({
14822
- stepName: `generate-handout-${options.lesson.lessonId}`,
14823
- fn: async () => generateHandoutFlow(options)
14824
- });
14825
- console.log(` \u2705 [Step: Handout] Done in ${((Date.now() - t0) / 1e3).toFixed(1)}s (Title: "${res.title}")`);
14826
- return res;
14827
- }
14828
- async function generateWorksheetStep(options) {
14829
- "use step";
14830
- return withRateLimitBackoff({
14831
- stepName: `generate-worksheet-${options.lesson.lessonId}`,
14832
- fn: async () => generateWorksheetFlow(options)
14833
- });
14834
- }
14835
- async function generateTeacherGuideStep(options) {
14836
- "use step";
14837
- return withRateLimitBackoff({
14838
- stepName: `generate-teacher-guide-${options.lesson.lessonId}`,
14839
- fn: async () => generateTeacherGuideFlow(options)
14840
- });
14841
- }
14842
- async function generateExtensionStep(options) {
14843
- "use step";
14844
- return withRateLimitBackoff({
14845
- stepName: `generate-extension-${options.lesson.lessonId}`,
14846
- fn: async () => generateExtensionFlow(options)
14847
- });
14848
- }
14849
-
14850
- // src/workflow/steps/satelliteJudgeStep.ts
14851
- async function judgeSatelliteStep(input) {
14852
- "use step";
14853
- const t0 = Date.now();
14854
- console.log(" \u{1F9D1}\u200D\u2696\uFE0F [Step: Satellite Judge] " + input.artifactType + " (" + input.artifactId + ")...");
14855
- return withRateLimitBackoff({
14856
- stepName: "judge-satellite-" + input.artifactType + "-" + input.artifactId,
14857
- fn: async () => {
14858
- return auditCurriculumQualityFlow({
14859
- targetArtifactType: input.artifactType,
14860
- lessonId: input.artifactId,
14861
- expectedLanguage: input.language,
14862
- targetObjectives: input.targetObjectives,
14863
- generatedContentJson: JSON.stringify(input.artifact),
14864
- standardStatements: input.standardStatements,
14865
- modelOptions: input.modelOptions
14712
+ if (quiz && quiz.quizId !== baseLessonId && !quiz.quizId.includes(baseLessonId)) {
14713
+ score -= 15;
14714
+ findings.push({
14715
+ id: "struct_quiz_id_mismatch",
14716
+ dimension: "CROSS_ARTIFACT_ZERO_DRIFT",
14717
+ severity: "MAJOR",
14718
+ title: "Quiz ID Contract Mismatch",
14719
+ description: `Quiz ID "${quiz.quizId}" does not match Lesson ID "${baseLessonId}".`,
14720
+ remediationAdvice: `Sync Quiz quizId to "${baseLessonId}".`,
14721
+ affectedElement: quiz.quizId
14866
14722
  });
14867
14723
  }
14868
- }).then((res) => {
14869
- console.log(" \u2705 [Satellite Judge] " + input.artifactType + " \u2192 " + res.overallVerdict + " (" + res.totalScore + "/100) in " + ((Date.now() - t0) / 1e3).toFixed(1) + "s");
14870
- return res;
14871
- });
14872
- }
14873
-
14874
- // src/workflow/steps/publishSteps.ts
14875
- async function saveMilestoneToWorkspaceStep(input) {
14876
- "use step";
14877
- const manager = new LocalWorkspaceManager(input.baseWorkspaceDir);
14878
- const savedFiles = [];
14879
- const lessonMd = serializeLessonToMarkdown(input.lesson);
14880
- const p1 = await manager.saveArtifact(input.jobId, input.milestoneSlug, "LESSON_5E.md", lessonMd, "LESSON");
14881
- savedFiles.push(p1);
14882
- if (input.activity) {
14883
- const actMd = serializeActivityToMarkdown(input.activity);
14884
- const p2 = await manager.saveArtifact(input.jobId, input.milestoneSlug, "ACT.md", actMd, "ACT");
14885
- savedFiles.push(p2);
14886
- }
14887
- if (input.codeLab) {
14888
- const labContent = serializeCodeLabToMarkdown(input.codeLab);
14889
- const p3 = await manager.saveArtifact(input.jobId, input.milestoneSlug, "LAB.md", labContent, "LAB");
14890
- savedFiles.push(p3);
14891
- }
14892
- if (input.selfLab) {
14893
- const selfLabMd = serializeSelfLabToMarkdown(input.selfLab);
14894
- const p4 = await manager.saveArtifact(input.jobId, input.milestoneSlug, "SELF_LAB.md", selfLabMd, "SELF_LAB");
14895
- savedFiles.push(p4);
14896
- }
14897
- if (input.quiz) {
14898
- const quizMd = serializeDiagnosticQuizToMarkdown(input.quiz);
14899
- const p5a = await manager.saveArtifact(input.jobId, input.milestoneSlug, "QUIZ.md", quizMd, "QUIZ_MD");
14900
- const p5b = await manager.saveArtifact(
14901
- input.jobId,
14902
- input.milestoneSlug,
14903
- "QUIZ.json",
14904
- JSON.stringify(input.quiz, null, 2),
14905
- "QUIZ_JSON"
14906
- );
14907
- savedFiles.push(p5a, p5b);
14908
- }
14909
- if (input.slides) {
14910
- const slidesContent = `---
14911
- marp: true
14912
- theme: default
14913
- paginate: true
14914
- ---
14915
-
14916
- # ${input.slides.title}
14917
-
14918
- ---
14919
-
14920
- ${input.slides.slides.map((s) => `## Slide ${s.slideNumber}: ${s.title}
14921
-
14922
- ${s.bulletPoints.map((b) => `- ${b}`).join("\n")}${s.codeSnippet ? `
14923
-
14924
- \`\`\`
14925
- ${s.codeSnippet}
14926
- \`\`\`` : ""}
14927
-
14928
- <!-- Presenter Notes: ${s.presenterNotes || ""} -->`).join("\n\n---\n\n")}
14929
- `.trim();
14930
- const p6 = await manager.saveArtifact(input.jobId, input.milestoneSlug, "SLIDE.md", slidesContent, "SLIDES");
14931
- savedFiles.push(p6);
14932
- }
14933
- if (input.handout) {
14934
- const handoutMd = serializeHandoutToMarkdown(input.handout);
14935
- const p7 = await manager.saveArtifact(input.jobId, input.milestoneSlug, "HANDOUT.md", handoutMd, "HANDOUT");
14936
- savedFiles.push(p7);
14937
- }
14938
- if (input.cheatSheetMarkdown) {
14939
- const p8 = await manager.saveArtifact(
14940
- input.jobId,
14941
- input.milestoneSlug,
14942
- "CHEAT_SHEET.md",
14943
- input.cheatSheetMarkdown,
14944
- "CHEAT_SHEET"
14945
- );
14946
- savedFiles.push(p8);
14947
- }
14948
- await manager.markMilestoneCompleted(input.jobId);
14949
- return {
14950
- milestoneSlug: input.milestoneSlug,
14951
- savedFiles
14952
- };
14953
- }
14954
- async function publishToGitStep(options) {
14955
- "use step";
14956
- return publishToGitHub(options);
14957
- }
14958
- async function publishToSupabaseStep(options) {
14959
- "use step";
14960
- return publishToSupabase(options);
14961
- }
14962
- var approvalPayloadSchema = z.object({
14963
- approved: z.boolean().describe("Whether the human reviewer approves the generated curriculum/lesson"),
14964
- reviewerName: z.string().optional().describe("Name or ID of the reviewer"),
14965
- feedback: z.string().optional().describe("Actionable feedback or required changes if rejected"),
14966
- timestamp: z.string().optional().describe("ISO timestamp of the approval action")
14967
- });
14968
- var lessonApprovalHook = defineHook();
14969
- function approvalHookToken(projectId, lessonId, artifactType) {
14970
- return `approval:${projectId}:${lessonId}:${artifactType}`;
14971
- }
14972
-
14973
- // src/standards/standardsCoverageGate.ts
14974
- function resolveStatementRef(ref, packs) {
14975
- const [head, ...rest] = ref.split(":");
14976
- const candidatePacks = rest.length > 0 ? packs.filter((p) => p.manifest.id === head) : packs;
14977
- const statementId = rest.length > 0 ? rest.join(":") : ref;
14978
- for (const p of candidatePacks) {
14979
- if (p.statements.some((s) => s.id === statementId)) return { pack: p, statementId };
14980
- }
14981
- return null;
14982
- }
14983
- function evaluateStandardsCoverage(input) {
14984
- const rows = [];
14985
- const aoToLo = /* @__PURE__ */ new Map();
14986
- for (const ao of input.assessmentObjectives ?? []) aoToLo.set(ao.aoCode, ao.alignedLO);
14987
- const loToRefs = /* @__PURE__ */ new Map();
14988
- for (const lo of input.objectives) {
14989
- loToRefs.set(lo.code, new Set(lo.standardRefs ?? []));
14990
- }
14991
- const taughtLOs = /* @__PURE__ */ new Set();
14992
- for (const act of input.activities ?? []) for (const lo of act.lo) taughtLOs.add(lo);
14993
- const assessedLOs = /* @__PURE__ */ new Set();
14994
- for (const q of input.quizQuestions) {
14995
- if (q.alignedLO) assessedLOs.add(q.alignedLO);
14996
- if (q.alignedAO) {
14997
- const lo = aoToLo.get(q.alignedAO);
14998
- if (lo) assessedLOs.add(lo);
14999
- }
15000
- }
15001
- for (const pack of input.packs) {
15002
- const mappingByStatement = /* @__PURE__ */ new Map();
15003
- for (const m of pack.mappings ?? []) {
15004
- const prev = mappingByStatement.get(m.statementId);
15005
- if (!prev || prev !== "covers" && m.kind === "covers" || prev === "extends" && m.kind !== "extends") {
15006
- mappingByStatement.set(m.statementId, m.kind);
15007
- }
14724
+ if (activity && activity.lessonId !== baseLessonId) {
14725
+ score -= 15;
14726
+ findings.push({
14727
+ id: "struct_act_id_mismatch",
14728
+ dimension: "CROSS_ARTIFACT_ZERO_DRIFT",
14729
+ severity: "MAJOR",
14730
+ title: "Activity Lesson ID Contract Mismatch",
14731
+ description: `Activity lessonId "${activity.lessonId}" does not match Lesson ID "${baseLessonId}".`,
14732
+ remediationAdvice: `Sync Activity lessonId to "${baseLessonId}".`,
14733
+ affectedElement: activity.lessonId
14734
+ });
15008
14735
  }
15009
- for (const statement of pack.statements) {
15010
- const refFull = `${pack.manifest.id}:${statement.id}`;
15011
- const issues = [];
15012
- const los = input.objectives.filter((lo) => (lo.standardRefs ?? []).some((r) => r === statement.id || r === refFull)).map((lo) => lo.code);
15013
- const kind = mappingByStatement.get(statement.id);
15014
- const hasMapping = kind !== void 0;
15015
- const isComplianceRelevant = kind === "covers";
15016
- const hasActivity = los.some((lo) => taughtLOs.has(lo));
15017
- const hasAssessment = los.some((lo) => assessedLOs.has(lo));
15018
- let status;
15019
- if (!hasMapping) status = "UNMAPPED";
15020
- else if (!isComplianceRelevant) status = "PARTIAL";
15021
- else if (los.length > 0 && hasActivity && hasAssessment) status = "COVERED";
15022
- else status = "UNCOVERED";
15023
- if (status === "UNCOVERED") {
15024
- if (los.length === 0) issues.push("No learning objective anchors this statement (missing standardRefs).");
15025
- else {
15026
- if (!hasActivity) issues.push(`Anchored LO(s) ${los.join(", ")} have no activity in lesson flow.`);
15027
- if (!hasAssessment) issues.push(`Anchored LO(s) ${los.join(", ")} are not measured by any quiz question.`);
15028
- }
15029
- }
15030
- rows.push({
15031
- packId: pack.manifest.id,
15032
- statementId: statement.id,
15033
- statementText: Object.values(statement.texts)[0] ?? "",
15034
- status,
15035
- objectives: los,
15036
- hasActivity,
15037
- hasAssessment,
15038
- issues
14736
+ if (slides && slides.lessonId !== baseLessonId) {
14737
+ score -= 15;
14738
+ findings.push({
14739
+ id: "struct_slides_id_mismatch",
14740
+ dimension: "CROSS_ARTIFACT_ZERO_DRIFT",
14741
+ severity: "MAJOR",
14742
+ title: "Slide Deck Lesson ID Contract Mismatch",
14743
+ description: `Slide deck lessonId "${slides.lessonId}" does not match Lesson ID "${baseLessonId}".`,
14744
+ remediationAdvice: `Sync Slide deck lessonId to "${baseLessonId}".`,
14745
+ affectedElement: slides.lessonId
15039
14746
  });
15040
14747
  }
15041
- }
15042
- for (const lo of input.objectives) {
15043
- for (const r of lo.standardRefs ?? []) {
15044
- if (!resolveStatementRef(r, input.packs)) {
15045
- rows.push({
15046
- packId: "(unresolved)",
15047
- statementId: r,
15048
- statementText: "",
15049
- status: "UNCOVERED",
15050
- objectives: [lo.code],
15051
- hasActivity: false,
15052
- hasAssessment: false,
15053
- issues: [`standardRef "${r}" does not exist in any adopted pack \u2014 likely a hallucinated or stale ID`]
14748
+ const satellites = [
14749
+ { type: "QUIZ", lang: quiz?.language },
14750
+ { type: "ACT", lang: activity?.language },
14751
+ { type: "SLIDE", lang: slides?.language }
14752
+ ];
14753
+ for (const sat of satellites) {
14754
+ if (sat.lang && sat.lang.toLowerCase() !== baseLanguage.toLowerCase()) {
14755
+ score -= 25;
14756
+ findings.push({
14757
+ id: `struct_language_mismatch_${sat.type}`,
14758
+ dimension: "CROSS_ARTIFACT_ZERO_DRIFT",
14759
+ severity: "CRITICAL",
14760
+ title: `Language Policy Inconsistency in ${sat.type}`,
14761
+ description: `Master Lesson is in "${baseLanguage}" but ${sat.type} is declared in "${sat.lang}".`,
14762
+ remediationAdvice: `Set ${sat.type} language to "${baseLanguage}".`
15054
14763
  });
15055
14764
  }
15056
14765
  }
15057
- }
15058
- const complianceRows = rows.filter((r) => r.status !== "UNMAPPED" && r.status !== "PARTIAL");
15059
- const covered = complianceRows.filter((r) => r.status === "COVERED").length;
15060
- const coveragePct = complianceRows.length === 0 ? 100 : Math.round(covered / complianceRows.length * 100);
15061
- const unresolvedCount = rows.filter((r) => r.packId === "(unresolved)").length;
15062
- const verdict = unresolvedCount > 0 || coveragePct < 60 ? "FAIL" : coveragePct < 100 ? "WARN" : "PASS";
15063
- const lines = [
15064
- "# Standards Coverage Report",
15065
- "",
15066
- `- Verdict: **${verdict}**`,
15067
- `- Compliance coverage: **${coveragePct}%** (${covered}/${complianceRows.length} "covers" statements fully taught + assessed)`,
15068
- `- Unresolved standardRefs: ${unresolvedCount}`,
15069
- "",
15070
- "| Pack | Statement | Status | LOs | Activity | Assessment |",
15071
- "|---|---|---|---|---|---|",
15072
- ...rows.map(
15073
- (r) => `|${r.packId}|${r.statementId}|${r.status}|${r.objectives.join(", ") || "\u2014"}|${r.hasActivity ? "\u2713" : "\u2717"}|${r.hasAssessment ? "\u2713" : "\u2717"}|`
15074
- )
15075
- ];
15076
- const issueLines = rows.filter((r) => r.issues.length > 0).flatMap((r) => r.issues.map((i) => `- [${r.packId}:${r.statementId}] ${i}`));
15077
- if (issueLines.length > 0) lines.push("", "## Issues", ...issueLines);
15078
- return {
15079
- verdict,
15080
- coveragePct,
15081
- rows,
15082
- summary: `Standards coverage ${coveragePct}% \u2014 verdict ${verdict}`,
15083
- rawMarkdownReport: lines.join("\n")
15084
- };
15085
- }
15086
- var StandardsRegistryAdapter = class {
15087
- client;
15088
- constructor(config = {}) {
15089
- if (config.client) {
15090
- this.client = config.client;
15091
- return;
15092
- }
15093
- const url = config.supabaseUrl || process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL || "";
15094
- const key = config.supabaseServiceRoleKey || process.env.SUPABASE_SERVICE_ROLE_KEY || config.supabaseAnonKey || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "";
15095
- if (!url || !key) {
15096
- throw new Error("StandardsRegistryAdapter: Supabase URL/key missing (pass client or env).");
14766
+ if (quiz && quiz.questions) {
14767
+ for (let i = 0; i < quiz.questions.length; i++) {
14768
+ const q = quiz.questions[i];
14769
+ const qId = q.id || `Q${i + 1}`;
14770
+ const options = q.options || [];
14771
+ if (options.length < 4) {
14772
+ score -= 10;
14773
+ findings.push({
14774
+ id: `struct_quiz_option_count_${qId}`,
14775
+ dimension: "MISCONCEPTION_RIGOR",
14776
+ severity: "MAJOR",
14777
+ title: `Structural Option Count Error in ${qId}`,
14778
+ description: `Question ${qId} has ${options.length} options (standard schema requires 4).`,
14779
+ remediationAdvice: "Ensure each question has 4 options (A, B, C, D).",
14780
+ affectedElement: qId
14781
+ });
14782
+ }
14783
+ const correctCount = options.filter((o) => o.isCorrect).length;
14784
+ if (correctCount !== 1) {
14785
+ score -= 20;
14786
+ findings.push({
14787
+ id: `struct_quiz_key_count_${qId}`,
14788
+ dimension: "MISCONCEPTION_RIGOR",
14789
+ severity: "CRITICAL",
14790
+ title: `Key Assignment Error in ${qId}`,
14791
+ description: `Question ${qId} has ${correctCount} correct options (must be exactly 1).`,
14792
+ remediationAdvice: "Set `isCorrect: true` on exactly 1 option.",
14793
+ affectedElement: qId
14794
+ });
14795
+ }
14796
+ }
15097
14797
  }
15098
- this.client = createClient(url, key);
15099
- }
15100
- // ─── Intake (write path — service role) ───────────────────────────────────
15101
- /** Persist a schema-validated pack as a new framework (status=draft). */
15102
- async importPack(pack, opts) {
15103
- const parsed = FrameworkPackSchema.safeParse(pack);
15104
- if (!parsed.success) {
15105
- throw new Error("importPack: pack failed schema validation: " + parsed.error.issues.map((i) => i.path.join(".") + ": " + i.message).join("; "));
14798
+ if (activity?.materialsBOM?.hardware && activity.materialsBOM.hardware.length > 0) {
14799
+ const hwText = activity.materialsBOM.hardware.join(" ").toLowerCase();
14800
+ const hasLED = hwText.includes("led");
14801
+ const hasResistor = hwText.includes("\u0111i\u1EC7n tr\u1EDF") || hwText.includes("resistor") || hwText.includes("220") || hwText.includes("330") || hwText.includes("1k");
14802
+ if (hasLED && !hasResistor) {
14803
+ score -= 20;
14804
+ findings.push({
14805
+ id: "struct_hardware_unsafe_led_no_resistor",
14806
+ dimension: "TECHNICAL_AUTHENTICITY",
14807
+ severity: "CRITICAL",
14808
+ title: "Unsafe Circuit BOM: LED without Current-Limiting Resistor",
14809
+ description: "Hardware BOM includes LED without a 220\u03A9-1k\u03A9 resistor, causing circuit overload.",
14810
+ remediationAdvice: "Add a 220\u03A9 current-limiting resistor to the hardware materials list."
14811
+ });
14812
+ }
15106
14813
  }
15107
- const p = parsed.data;
15108
- const { data: fw, error: fwErr } = await this.client.from("standards_frameworks").insert({
15109
- pack_id: p.manifest.id,
15110
- content_version: p.manifest.contentVersion,
15111
- name: p.manifest.name,
15112
- spec_version: p.manifest.specVersion,
15113
- subject: p.manifest.subject,
15114
- languages: p.manifest.languages,
15115
- grade_model: p.manifest.gradeModel,
15116
- provenance: p.manifest.provenance,
15117
- trust: p.manifest.trust,
15118
- status: "draft",
15119
- organization_code: opts?.organizationCode ?? (p.manifest.trust === "verified" ? null : p.manifest.provenance.importedBy?.replace(/^org:/, "") || null),
15120
- original_file_path: opts?.originalFilePath ?? null,
15121
- created_by: opts?.createdBy ?? null
15122
- }).select("id").single();
15123
- if (fwErr) throw new Error("importPack: framework insert failed: " + fwErr.message);
15124
- const frameworkId = fw.id;
15125
- const statementRows = p.statements.map((s) => ({
15126
- framework_id: frameworkId,
15127
- statement_id: s.id,
15128
- parent_statement_id: s.parentId ?? null,
15129
- grade_min: s.gradeBand[0],
15130
- grade_max: s.gradeBand[1],
15131
- texts: s.texts,
15132
- classifications: s.classifications ?? [],
15133
- bloom_hint: s.bloomHint ?? null,
15134
- keywords: s.keywords ?? [],
15135
- source_ref: s.sourceRef ?? null,
15136
- provenance: s.provenance
15137
- }));
15138
- const { error: stErr, count: stCount } = await this.client.from("standards_statements").insert(statementRows, { count: "exact" });
15139
- if (stErr) throw new Error("importPack: statement insert failed: " + stErr.message);
15140
- let mappingCount = 0;
15141
- if (p.mappings && p.mappings.length > 0) {
15142
- const mappingRows = p.mappings.map((m) => ({
15143
- framework_id: frameworkId,
15144
- statement_id: m.statementId,
15145
- target_ref: m.targetRef,
15146
- kind: m.kind,
15147
- confidence: m.confidence,
15148
- provenance: m.provenance
15149
- }));
15150
- const { error: mpErr, count: mpCount } = await this.client.from("standards_mappings").insert(mappingRows, { count: "exact" });
15151
- if (mpErr) throw new Error("importPack: mapping insert failed: " + mpErr.message);
15152
- mappingCount = mpCount ?? mappingRows.length;
14814
+ score = Math.max(0, Math.min(100, score));
14815
+ const passed = score >= 80 && !findings.some((f) => f.severity === "CRITICAL");
14816
+ if (passed) {
14817
+ strengths.push("Deterministic structure, ID contracts, and schema invariants strictly verified.");
15153
14818
  }
15154
- return { frameworkDbId: frameworkId, statementsInserted: stCount ?? statementRows.length, mappingsInserted: mappingCount };
15155
- }
15156
- /** Activate a draft framework (immutable version is now live). */
15157
- async activatePack(frameworkDbId) {
15158
- const { error } = await this.client.from("standards_frameworks").update({ status: "active" }).eq("id", frameworkDbId).eq("status", "draft");
15159
- if (error) throw new Error("activatePack failed: " + error.message);
15160
- }
15161
- async deprecatePack(frameworkDbId) {
15162
- const { error } = await this.client.from("standards_frameworks").update({ status: "deprecated" }).eq("id", frameworkDbId);
15163
- if (error) throw new Error("deprecatePack failed: " + error.message);
15164
- }
15165
- async adoptPack(frameworkDbId, organizationCode, adoptedBy) {
15166
- const { error } = await this.client.from("standards_org_adoptions").upsert(
15167
- { framework_id: frameworkDbId, organization_code: organizationCode, adopted_by: adoptedBy ?? null },
15168
- { onConflict: "organization_code,framework_id" }
15169
- );
15170
- if (error) throw new Error("adoptPack failed: " + error.message);
14819
+ return {
14820
+ passed,
14821
+ structuralScore: score,
14822
+ findings,
14823
+ strengths
14824
+ };
15171
14825
  }
15172
- // ─── Runtime (read path — generation) ─────────────────────────────────────
14826
+ };
14827
+
14828
+ // src/evaluators/academicAuditor.ts
14829
+ var AcademicAuditor = class {
15173
14830
  /**
15174
- * Load all packs usable by an org: platform packs (verified, org IS NULL) +
15175
- * org packs + org adoptions. Hydrated into the same FrameworkPack shape the
15176
- * in-memory Phase-1 components consume (injector / coverage gate / judge).
14831
+ * Evaluates a complete lesson bundle.
14832
+ * Step 1 (Deterministic): Fast structural, contract, schema, and safety linting.
14833
+ * Step 2 (Semantic LLM-as-a-Judge): Deep pedagogical, cognitive (Bloom), and misconception analysis with live frontier LLM.
15177
14834
  */
15178
- async loadPacksForOrg(organizationCode) {
15179
- let query = this.client.from("standards_frameworks").select("*, standards_org_adoptions(organization_code)").eq("status", "active");
15180
- const { data: frameworks, error } = await query;
15181
- if (error) throw new Error("loadPacksForOrg: " + error.message);
15182
- const visible = (frameworks ?? []).filter((f) => {
15183
- const own = f.organization_code && organizationCode && f.organization_code === organizationCode;
15184
- const global = !f.organization_code;
15185
- const adopted = organizationCode && (f.standards_org_adoptions ?? []).some((a) => a.organization_code === organizationCode);
15186
- return global || own || adopted;
14835
+ static async auditLessonBundle(input) {
14836
+ const { lesson, quiz, activity, slides, codeLab, modelOptions, executeLLMJudge = true } = input;
14837
+ const structuralResult = DeterministicStructuralLinter.lintBundle({
14838
+ lesson,
14839
+ quiz,
14840
+ activity,
14841
+ slides,
14842
+ codeLab
15187
14843
  });
15188
- if (visible.length === 0) return [];
15189
- const ids = visible.map((f) => f.id);
15190
- const [{ data: statements, error: stErr }, { data: mappings, error: mpErr }, { data: overrides, error: ovErr }] = await Promise.all([
15191
- this.client.from("standards_statements").select("*").in("framework_id", ids),
15192
- this.client.from("standards_mappings").select("*").in("framework_id", ids),
15193
- this.client.from("standards_mapping_overrides").select("*").in("framework_id", ids)
15194
- ]);
15195
- if (stErr) throw new Error("loadPacksForOrg statements: " + stErr.message);
15196
- const overridesByFw = /* @__PURE__ */ new Map();
15197
- for (const o of overrides ?? []) {
15198
- const list = overridesByFw.get(o.framework_id) ?? [];
15199
- list.push(o);
15200
- overridesByFw.set(o.framework_id, list);
15201
- }
15202
- return visible.map((f) => {
15203
- const stmts = (statements ?? []).filter((s) => s.framework_id === f.id).map((s) => ({
15204
- id: s.statement_id,
15205
- parentId: s.parent_statement_id ?? void 0,
15206
- gradeBand: [s.grade_min, s.grade_max],
15207
- texts: s.texts,
15208
- classifications: s.classifications ?? [],
15209
- bloomHint: s.bloom_hint ?? void 0,
15210
- keywords: s.keywords ?? [],
15211
- sourceRef: s.source_ref ?? void 0,
15212
- provenance: s.provenance ?? { method: "imported" }
15213
- }));
15214
- const ov = overridesByFw.get(f.id) ?? [];
15215
- const retired = new Set(ov.filter((o) => o.kind === "retired").map((o) => o.statement_id + "|" + o.target_ref));
15216
- const embedded = (mappings ?? []).filter((m) => m.framework_id === f.id).map((m) => ({
15217
- statementId: m.statement_id,
15218
- targetRef: m.target_ref,
15219
- kind: m.kind,
15220
- confidence: Number(m.confidence),
15221
- provenance: m.provenance ?? { method: "imported" }
15222
- }));
15223
- const overlay = ov.filter((o) => o.kind !== "retired").map((o) => ({
15224
- statementId: o.statement_id,
15225
- targetRef: o.target_ref,
15226
- kind: o.kind,
15227
- confidence: Number(o.confidence),
15228
- provenance: { method: "human", reviewedBy: o.created_by ?? "overlay", note: o.note ?? void 0 }
15229
- }));
15230
- const bridge = /* @__PURE__ */ new Map();
15231
- for (const m of embedded) bridge.set(m.statementId + "|" + m.targetRef, m);
15232
- for (const m of overlay) bridge.set(m.statementId + "|" + m.targetRef, m);
15233
- const finalMappings = [...bridge.values()].filter((m) => !retired.has(m.statementId + "|" + m.targetRef));
15234
- const hydrated = FrameworkPackSchema.safeParse({
15235
- manifest: {
15236
- id: f.pack_id,
15237
- name: f.name,
15238
- specVersion: f.spec_version || "1.0",
15239
- contentVersion: f.content_version,
15240
- subject: f.subject,
15241
- languages: f.languages,
15242
- gradeModel: f.grade_model,
15243
- provenance: f.provenance,
15244
- trust: f.trust
15245
- },
15246
- statements: stmts,
15247
- mappings: finalMappings
15248
- });
15249
- if (!hydrated.success) {
15250
- throw new Error('loadPacksForOrg: hydrated pack "' + f.pack_id + '" failed schema: ' + hydrated.error.issues[0]?.message);
15251
- }
15252
- return { ...hydrated.data, dbId: f.id, status: f.status, organizationCode: f.organization_code ?? null };
15253
- });
15254
- }
15255
- };
15256
-
15257
- // src/workflow/workflows/milestoneWorkflow.ts
15258
- async function generateMilestoneWorkflow(input) {
15259
- "use workflow";
15260
- const language = input.language || "vi";
15261
- const milestoneId = input.milestone.id || input.milestone.concept_code || "L01";
15262
- const slugPrefix = /^\d+$/.test(milestoneId) ? `M${milestoneId.padStart(2, "0")}` : milestoneId;
15263
- const milestoneSlug = `${slugPrefix}_${(input.milestone.name || "milestone").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "")}`;
15264
- const bundleTier = input.bundleTier || "minimum";
15265
- const gates = resolveGateSettings(input.gateSettings);
15266
- const techStack = input.techStack || input.milestone.tech_keywords || ["General Technology"];
15267
- console.log(" \u{1F6A6} [Gate] LESSON: " + gates.LESSON + " | ACT: " + gates.ACT + " | QUIZ: " + gates.QUIZ);
15268
- const targetObjectives = (input.milestone.learning_objectives || []).map((lo) => ({
15269
- code: lo.code,
15270
- description: lo.description || lo.name || "",
15271
- bloomLevel: lo.bloom_level || "understand"
15272
- }));
15273
- const packs = input.standardsPacks ?? [];
15274
- const { block: standardsContext, selected: selectedStatements } = buildStandardsContext({
15275
- packs,
15276
- gradeBand: input.milestone.grade_band ?? [6, 12],
15277
- topicText: [input.topic, input.milestone.name, input.milestone.description, ...input.milestone.tech_keywords || []].filter(Boolean).join(" "),
15278
- conceptCodes: (input.milestone.concept_code || "").split(",").map((c) => c.trim()).filter(Boolean),
15279
- language
15280
- });
15281
- const standardStatements = {};
15282
- for (const s of selectedStatements) {
15283
- standardStatements[s.packId + ":" + s.statement.id] = Object.values(s.statement.texts)[0] || "";
15284
- }
15285
- if (selectedStatements.length > 0) {
15286
- console.log(" \u{1F4D0} [Standards] Grounded with " + selectedStatements.length + " verbatim statement(s) from " + new Set(selectedStatements.map((s) => s.packId)).size + " pack(s)");
15287
- }
15288
- let lesson = await generateMasterLessonStep({
15289
- milestone: input.milestone,
15290
- language,
15291
- topic: input.topic,
15292
- targetAudience: input.targetAudience,
15293
- standardsContext: standardsContext || void 0,
15294
- modelOptions: input.modelOptions
15295
- });
15296
- let auditReport;
15297
- const lessonGate = gateModeFor(gates, "LESSON");
15298
- if (lessonGate === "LLM_JUDGE") {
15299
- auditReport = await judgeMasterLessonStep({
15300
- lessonId: milestoneId,
15301
- lesson,
15302
- language,
15303
- targetObjectives,
15304
- standardStatements: Object.keys(standardStatements).length > 0 ? standardStatements : void 0,
15305
- modelOptions: input.modelOptions
15306
- });
15307
- if (auditReport.overallVerdict === "FAIL" || auditReport.overallVerdict === "NEEDS_REVISION") {
15308
- lesson = await repairMasterLessonStep({
15309
- milestone: input.milestone,
15310
- language,
15311
- topic: input.topic,
15312
- targetAudience: input.targetAudience,
15313
- auditReport,
15314
- standardsContext: standardsContext || void 0,
15315
- modelOptions: input.modelOptions
15316
- });
15317
- }
15318
- } else if (lessonGate === "HITL") {
15319
- const hookToken = "gate:lesson:" + milestoneId;
15320
- console.log(" \u23F8 [Gate] LESSON awaiting HUMAN review (HITL) \u2014 hook " + hookToken);
15321
- const workflowPkg = await import('workflow');
15322
- const createHook = workflowPkg.createHook;
15323
- const hook = createHook({ token: hookToken });
15324
- const review = await hook;
15325
- console.log(" \u2705 [Gate] Human verdict: " + (review.approved ? "APPROVED" : "REJECTED") + (review.feedback ? " \u2014 " + review.feedback : ""));
15326
- if (!review.approved) {
15327
- const repairReport = {
15328
- overallVerdict: "NEEDS_REVISION",
15329
- totalScore: 0,
15330
- languageAdherencePassed: true,
15331
- criteria: [],
15332
- actionableRepairPrompts: review.feedback ? [review.feedback] : ["S\u1EEDa theo ph\u1EA3n h\u1ED3i c\u1EE7a ng\u01B0\u1EDDi duy\u1EC7t"]
15333
- };
15334
- lesson = await repairMasterLessonStep({
15335
- milestone: input.milestone,
15336
- language,
15337
- topic: input.topic,
15338
- targetAudience: input.targetAudience,
15339
- auditReport: repairReport,
15340
- standardsContext: standardsContext || void 0,
15341
- modelOptions: input.modelOptions
15342
- });
15343
- const hook2 = createHook({ token: hookToken + ":v2" });
15344
- const review2 = await hook2;
15345
- console.log(" \u2705 [Gate] Human verdict (v2): " + (review2.approved ? "APPROVED" : "REJECTED"));
15346
- if (!review2.approved) {
15347
- console.log(" \u{1F6D1} [Gate] LESSON rejected twice \u2014 satellites & save BLOCKED");
15348
- return {
15349
- milestoneId,
15350
- milestoneSlug,
15351
- status: "FAILED",
15352
- gateBlocked: { artifactType: "LESSON", reason: "HITL_REJECTED_V2", feedback: review2.feedback },
15353
- artifacts: void 0
15354
- };
15355
- }
15356
- }
15357
- }
15358
- const [activity, selfLab, quiz, slides, handout] = await Promise.all([
15359
- generateActivityStep({ lesson, language, modelOptions: input.modelOptions }),
15360
- generateSelfLabStep({ milestone: input.milestone, language, targetTechStack: techStack, modelOptions: input.modelOptions }),
15361
- generateDiagnosticQuizStep({ milestone: input.milestone, language, modelOptions: input.modelOptions }),
15362
- generateSlidesStep({ lesson, language, modelOptions: input.modelOptions }),
15363
- generateHandoutStep({ lesson, language, modelOptions: input.modelOptions })
15364
- ]);
15365
- const codeLab = await generateCodeLabStep({
15366
- lesson,
15367
- activity,
15368
- language,
15369
- targetTechStack: techStack,
15370
- modelOptions: input.modelOptions
15371
- });
15372
- const satelliteReports = {};
15373
- const satelliteCandidates = [
15374
- { type: "ACT", id: "ACT_" + milestoneId, artifact: activity },
15375
- { type: "QUIZ", id: "QUIZ_" + milestoneId, artifact: quiz },
15376
- { type: "SLIDE", id: "SLIDE_" + milestoneId, artifact: slides },
15377
- { type: "HANDOUT", id: "HANDOUT_" + milestoneId, artifact: handout },
15378
- { type: "CODE", id: "CODE_" + milestoneId, artifact: codeLab }
15379
- ];
15380
- const toJudge = satelliteCandidates.filter((c) => c.artifact && gateModeFor(gates, c.type) === "LLM_JUDGE");
15381
- if (toJudge.length > 0) {
15382
- const reports = await Promise.all(
15383
- toJudge.map(
15384
- (c) => judgeSatelliteStep({
15385
- artifactType: c.type,
15386
- artifactId: c.id,
15387
- artifact: c.artifact,
15388
- language,
15389
- targetObjectives,
15390
- standardStatements: Object.keys(standardStatements).length > 0 ? standardStatements : void 0,
15391
- modelOptions: input.modelOptions
15392
- }).catch((err) => {
15393
- console.warn(" \u26A0 [Gate] satellite judge failed for " + c.type + ": " + err.message);
15394
- return void 0;
15395
- })
15396
- )
15397
- );
15398
- for (let i = 0; i < toJudge.length; i++) {
15399
- const r = reports[i];
15400
- if (r) satelliteReports[toJudge[i].type] = r;
15401
- }
15402
- }
15403
- let worksheet;
15404
- let teacherGuide;
15405
- let extension;
15406
- if (bundleTier === "full") {
15407
- [worksheet, teacherGuide, extension] = await Promise.all([
15408
- generateWorksheetStep({ lesson, language, modelOptions: input.modelOptions }),
15409
- generateTeacherGuideStep({ lesson, activity, language, modelOptions: input.modelOptions }),
15410
- generateExtensionStep({ lesson, activity, language, modelOptions: input.modelOptions })
15411
- ]);
15412
- }
15413
- let standardsCoverage;
15414
- if (packs.length > 0) {
15415
- standardsCoverage = evaluateStandardsCoverage({
15416
- packs,
15417
- objectives: (lesson.learningObjectives || []).map((lo) => ({
15418
- code: lo.code,
15419
- standardRefs: lo.standardRefs,
15420
- conceptRefs: lo.conceptRefs
15421
- })),
15422
- // NOTE: ActivityLab has no structured LO linkage yet — activity coverage rows are derived
15423
- // from LO→quiz paths inside the gate. Wire ACT.loRefs in a future schema revision.
15424
- activities: [],
15425
- quizQuestions: (quiz?.questions || []).map((q) => ({ alignedLO: q.alignedLO, alignedAO: q.alignedAO })),
15426
- assessmentObjectives: quiz?.assessmentObjectives?.map((ao) => ({ aoCode: ao.aoCode, alignedLO: ao.alignedLO }))
15427
- });
15428
- console.log(" \u{1F4D0} [Standards Coverage] " + standardsCoverage.summary);
15429
- const enforce = input.enforceStandardsCoverage ?? true;
15430
- if (enforce && standardsCoverage.verdict === "FAIL") {
15431
- console.error(" \u26D4 [Standards Coverage] HARD BLOCK \u2014 " + standardsCoverage.summary);
15432
- console.error(standardsCoverage.rawMarkdownReport.split("\n").filter((l) => l.startsWith("- [")).slice(0, 10).join("\n"));
15433
- return {
15434
- milestoneId,
15435
- milestoneSlug,
15436
- status: "FAILED",
15437
- savedResult: { workspaceDir: "", files: [] },
15438
- judgeReport: auditReport,
15439
- standardsCoverage,
15440
- artifacts: { lesson, activity, codeLab, selfLab, quiz, slides, handout, worksheet, teacherGuide, extension }
15441
- };
15442
- }
15443
- }
15444
- const savedResult = await saveMilestoneToWorkspaceStep({
15445
- jobId: input.jobId,
15446
- milestoneSlug,
15447
- lesson,
15448
- activity,
15449
- codeLab,
15450
- selfLab,
15451
- quiz,
15452
- slides,
15453
- handout,
15454
- baseWorkspaceDir: input.baseWorkspaceDir
15455
- });
15456
- return {
15457
- milestoneId,
15458
- milestoneSlug,
15459
- status: "SUCCESS",
15460
- savedResult,
15461
- judgeReport: auditReport,
15462
- satelliteJudgeReports: Object.keys(satelliteReports).length > 0 ? satelliteReports : void 0,
15463
- gatesResolved: gates,
15464
- standardsCoverage,
15465
- artifacts: {
15466
- lesson,
15467
- activity,
15468
- codeLab,
15469
- selfLab,
15470
- quiz,
15471
- slides,
15472
- handout,
15473
- worksheet,
15474
- teacherGuide,
15475
- extension
15476
- }
15477
- };
15478
- }
15479
- async function generateRoadmapWorkflow(input) {
15480
- "use workflow";
15481
- const jobId = input.jobId || `job_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;
15482
- const language = input.language || input.roadmap.target_language || "vi";
15483
- const topic = input.roadmap.topic || input.roadmap.goal || "General Curriculum";
15484
- const courseTitle = input.roadmap.title || `Curriculum: ${topic}`;
15485
- const batchSize = input.batchSize || 2;
15486
- const milestones = input.roadmap.milestones || [];
15487
- const workspaceManager = new LocalWorkspaceManager(input.baseWorkspaceDir);
15488
- const workspaceDir = await workspaceManager.initJobWorkspace(jobId, {
15489
- courseTitle,
15490
- topic,
15491
- language,
15492
- totalMilestones: milestones.length
15493
- });
15494
- const milestoneOutcomes = [];
15495
- for (let i = 0; i < milestones.length; i += batchSize) {
15496
- const batch = milestones.slice(i, i + batchSize);
15497
- const batchSettled = await Promise.allSettled(
15498
- batch.map(
15499
- (milestone) => generateMilestoneWorkflow({
15500
- jobId,
15501
- milestone,
15502
- language,
15503
- topic,
15504
- targetAudience: input.roadmap.target_audience,
15505
- techStack: input.roadmap.tech_stack || milestone.tech_keywords,
15506
- bundleTier: input.bundleTier,
15507
- gateSettings: input.gateSettings,
15508
- baseWorkspaceDir: input.baseWorkspaceDir,
15509
- modelOptions: input.modelOptions
15510
- })
15511
- )
15512
- );
15513
- for (const [idx, outcome] of batchSettled.entries()) {
15514
- const targetMilestone = batch[idx];
15515
- const mId = targetMilestone.id || targetMilestone.concept_code || `M${i + idx + 1}`;
15516
- if (outcome.status === "fulfilled") {
15517
- milestoneOutcomes.push({
15518
- milestoneId: mId,
15519
- status: "FULFILLED",
15520
- result: outcome.value
15521
- });
15522
- } else {
15523
- milestoneOutcomes.push({
15524
- milestoneId: mId,
15525
- status: "REJECTED",
15526
- error: outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason)
15527
- });
15528
- }
15529
- }
15530
- if (i + batchSize < milestones.length) {
15531
- await sleep("2s");
15532
- }
15533
- }
15534
- const successfulMilestones = milestoneOutcomes.filter((m) => m.status === "FULFILLED").length;
15535
- const failedMilestones = milestoneOutcomes.filter((m) => m.status === "REJECTED").length;
15536
- let gitPublishResult = void 0;
15537
- let supabasePublishResult = void 0;
15538
- if (input.gitPublishOptions) {
15539
- gitPublishResult = await publishToGitStep({
15540
- jobId,
15541
- localJobDir: workspaceDir,
15542
- ...input.gitPublishOptions
15543
- });
15544
- }
15545
- if (input.supabasePublishOptions) {
15546
- supabasePublishResult = await publishToSupabaseStep({
15547
- jobId,
15548
- courseTitle,
15549
- topic,
15550
- language,
15551
- milestones: milestoneOutcomes.filter((m) => m.status === "FULFILLED" && m.result).map((m) => ({
15552
- milestoneId: m.milestoneId,
15553
- milestoneName: m.result.savedResult.milestoneSlug,
15554
- lessonSlug: m.result.savedResult.milestoneSlug
15555
- })),
15556
- ...input.supabasePublishOptions
15557
- });
15558
- }
15559
- return {
15560
- jobId,
15561
- courseTitle,
15562
- topic,
15563
- language,
15564
- totalMilestones: milestones.length,
15565
- successfulMilestones,
15566
- failedMilestones,
15567
- results: milestoneOutcomes,
15568
- gitPublishResult,
15569
- supabasePublishResult,
15570
- workspaceDir
15571
- };
15572
- }
15573
-
15574
- // src/workflow/workflows/singleArtifactWorkflow.ts
15575
- async function executeSingleArtifactStep(request) {
15576
- "use step";
15577
- const t0 = Date.now();
15578
- console.log(` \u23F3 [Step: Single Artifact] Generating ${request.artifactType.toUpperCase()} for "${request.topic.slice(0, 35)}"...`);
15579
- const res = await withRateLimitBackoff({
15580
- stepName: `generate-single-artifact-${request.artifactType}-${(request.topic || "topic").slice(0, 20)}`,
15581
- fn: async () => generateSingleArtifact(request)
15582
- });
15583
- console.log(` \u2705 [Step: Single Artifact] Done in ${((Date.now() - t0) / 1e3).toFixed(1)}s (File: "${res.filename}")`);
15584
- return res;
15585
- }
15586
- async function generateSingleArtifactWorkflow(request) {
15587
- "use workflow";
15588
- return executeSingleArtifactStep(request);
15589
- }
15590
-
15591
- // src/index.ts
15592
- init_errors();
15593
-
15594
- // src/evaluators/deterministicStructuralLinter.ts
15595
- var DeterministicStructuralLinter = class {
15596
- /**
15597
- * Validates structural invariants across a complete lesson bundle.
15598
- */
15599
- static lintBundle(input) {
15600
- const { lesson, quiz, activity, slides, codeLab } = input;
15601
- const findings = [];
15602
- const strengths = [];
15603
- let score = 100;
15604
- const baseLessonId = lesson.lessonId;
15605
- const baseLanguage = lesson.language;
15606
- if (!lesson.title || lesson.title.trim().length === 0) {
15607
- score -= 20;
15608
- findings.push({
15609
- id: "struct_missing_lesson_title",
15610
- dimension: "CROSS_ARTIFACT_ZERO_DRIFT",
15611
- severity: "CRITICAL",
15612
- title: "Missing Lesson Title",
15613
- description: "Lesson plan has an empty or whitespace title.",
15614
- remediationAdvice: "Provide a non-empty lesson title."
15615
- });
15616
- }
15617
- if (!lesson.learningObjectives || lesson.learningObjectives.length === 0) {
15618
- score -= 25;
15619
- findings.push({
15620
- id: "struct_empty_learning_objectives",
15621
- dimension: "CONSTRUCTIVE_ALIGNMENT",
15622
- severity: "CRITICAL",
15623
- title: "Empty Learning Objectives Array",
15624
- description: "Lesson plan contains 0 learning objectives.",
15625
- remediationAdvice: "Declare at least 2 structured Learning Objectives in the schema."
15626
- });
15627
- }
15628
- if (!lesson.sections || lesson.sections.length === 0) {
15629
- score -= 25;
15630
- findings.push({
15631
- id: "struct_empty_lesson_sections",
15632
- dimension: "5E_INSTRUCTIONAL_FIDELITY",
15633
- severity: "CRITICAL",
15634
- title: "Empty Lesson Sections Array",
15635
- description: "Lesson plan contains no instructional sections.",
15636
- remediationAdvice: "Provide structured lesson flow sections."
15637
- });
15638
- }
15639
- if (quiz && quiz.quizId !== baseLessonId && !quiz.quizId.includes(baseLessonId)) {
15640
- score -= 15;
15641
- findings.push({
15642
- id: "struct_quiz_id_mismatch",
15643
- dimension: "CROSS_ARTIFACT_ZERO_DRIFT",
15644
- severity: "MAJOR",
15645
- title: "Quiz ID Contract Mismatch",
15646
- description: `Quiz ID "${quiz.quizId}" does not match Lesson ID "${baseLessonId}".`,
15647
- remediationAdvice: `Sync Quiz quizId to "${baseLessonId}".`,
15648
- affectedElement: quiz.quizId
15649
- });
15650
- }
15651
- if (activity && activity.lessonId !== baseLessonId) {
15652
- score -= 15;
15653
- findings.push({
15654
- id: "struct_act_id_mismatch",
15655
- dimension: "CROSS_ARTIFACT_ZERO_DRIFT",
15656
- severity: "MAJOR",
15657
- title: "Activity Lesson ID Contract Mismatch",
15658
- description: `Activity lessonId "${activity.lessonId}" does not match Lesson ID "${baseLessonId}".`,
15659
- remediationAdvice: `Sync Activity lessonId to "${baseLessonId}".`,
15660
- affectedElement: activity.lessonId
15661
- });
15662
- }
15663
- if (slides && slides.lessonId !== baseLessonId) {
15664
- score -= 15;
15665
- findings.push({
15666
- id: "struct_slides_id_mismatch",
15667
- dimension: "CROSS_ARTIFACT_ZERO_DRIFT",
15668
- severity: "MAJOR",
15669
- title: "Slide Deck Lesson ID Contract Mismatch",
15670
- description: `Slide deck lessonId "${slides.lessonId}" does not match Lesson ID "${baseLessonId}".`,
15671
- remediationAdvice: `Sync Slide deck lessonId to "${baseLessonId}".`,
15672
- affectedElement: slides.lessonId
15673
- });
15674
- }
15675
- const satellites = [
15676
- { type: "QUIZ", lang: quiz?.language },
15677
- { type: "ACT", lang: activity?.language },
15678
- { type: "SLIDE", lang: slides?.language }
15679
- ];
15680
- for (const sat of satellites) {
15681
- if (sat.lang && sat.lang.toLowerCase() !== baseLanguage.toLowerCase()) {
15682
- score -= 25;
15683
- findings.push({
15684
- id: `struct_language_mismatch_${sat.type}`,
15685
- dimension: "CROSS_ARTIFACT_ZERO_DRIFT",
15686
- severity: "CRITICAL",
15687
- title: `Language Policy Inconsistency in ${sat.type}`,
15688
- description: `Master Lesson is in "${baseLanguage}" but ${sat.type} is declared in "${sat.lang}".`,
15689
- remediationAdvice: `Set ${sat.type} language to "${baseLanguage}".`
15690
- });
15691
- }
15692
- }
15693
- if (quiz && quiz.questions) {
15694
- for (let i = 0; i < quiz.questions.length; i++) {
15695
- const q = quiz.questions[i];
15696
- const qId = q.id || `Q${i + 1}`;
15697
- const options = q.options || [];
15698
- if (options.length < 4) {
15699
- score -= 10;
15700
- findings.push({
15701
- id: `struct_quiz_option_count_${qId}`,
15702
- dimension: "MISCONCEPTION_RIGOR",
15703
- severity: "MAJOR",
15704
- title: `Structural Option Count Error in ${qId}`,
15705
- description: `Question ${qId} has ${options.length} options (standard schema requires 4).`,
15706
- remediationAdvice: "Ensure each question has 4 options (A, B, C, D).",
15707
- affectedElement: qId
15708
- });
15709
- }
15710
- const correctCount = options.filter((o) => o.isCorrect).length;
15711
- if (correctCount !== 1) {
15712
- score -= 20;
15713
- findings.push({
15714
- id: `struct_quiz_key_count_${qId}`,
15715
- dimension: "MISCONCEPTION_RIGOR",
15716
- severity: "CRITICAL",
15717
- title: `Key Assignment Error in ${qId}`,
15718
- description: `Question ${qId} has ${correctCount} correct options (must be exactly 1).`,
15719
- remediationAdvice: "Set `isCorrect: true` on exactly 1 option.",
15720
- affectedElement: qId
15721
- });
15722
- }
15723
- }
15724
- }
15725
- if (activity?.materialsBOM?.hardware && activity.materialsBOM.hardware.length > 0) {
15726
- const hwText = activity.materialsBOM.hardware.join(" ").toLowerCase();
15727
- const hasLED = hwText.includes("led");
15728
- const hasResistor = hwText.includes("\u0111i\u1EC7n tr\u1EDF") || hwText.includes("resistor") || hwText.includes("220") || hwText.includes("330") || hwText.includes("1k");
15729
- if (hasLED && !hasResistor) {
15730
- score -= 20;
15731
- findings.push({
15732
- id: "struct_hardware_unsafe_led_no_resistor",
15733
- dimension: "TECHNICAL_AUTHENTICITY",
15734
- severity: "CRITICAL",
15735
- title: "Unsafe Circuit BOM: LED without Current-Limiting Resistor",
15736
- description: "Hardware BOM includes LED without a 220\u03A9-1k\u03A9 resistor, causing circuit overload.",
15737
- remediationAdvice: "Add a 220\u03A9 current-limiting resistor to the hardware materials list."
15738
- });
15739
- }
15740
- }
15741
- score = Math.max(0, Math.min(100, score));
15742
- const passed = score >= 80 && !findings.some((f) => f.severity === "CRITICAL");
15743
- if (passed) {
15744
- strengths.push("Deterministic structure, ID contracts, and schema invariants strictly verified.");
15745
- }
15746
- return {
15747
- passed,
15748
- structuralScore: score,
15749
- findings,
15750
- strengths
15751
- };
15752
- }
15753
- };
15754
-
15755
- // src/evaluators/academicAuditor.ts
15756
- var AcademicAuditor = class {
15757
- /**
15758
- * Evaluates a complete lesson bundle.
15759
- * Step 1 (Deterministic): Fast structural, contract, schema, and safety linting.
15760
- * Step 2 (Semantic LLM-as-a-Judge): Deep pedagogical, cognitive (Bloom), and misconception analysis with live frontier LLM.
15761
- */
15762
- static async auditLessonBundle(input) {
15763
- const { lesson, quiz, activity, slides, codeLab, modelOptions, executeLLMJudge = true } = input;
15764
- const structuralResult = DeterministicStructuralLinter.lintBundle({
15765
- lesson,
15766
- quiz,
15767
- activity,
15768
- slides,
15769
- codeLab
15770
- });
15771
- const allFindings = [...structuralResult.findings];
15772
- const strengths = [...structuralResult.strengths];
15773
- let semanticScore = null;
15774
- let semanticVerdict = "PASS";
15775
- if (executeLLMJudge) {
15776
- try {
15777
- const judgeReport = await auditCurriculumQualityFlow({
15778
- targetArtifactType: "LESSON_BUNDLE",
15779
- lessonId: lesson.lessonId,
15780
- expectedLanguage: lesson.language,
15781
- targetObjectives: (lesson.learningObjectives || []).map((lo) => ({
15782
- code: lo.code,
15783
- description: lo.description,
15784
- bloomLevel: lo.bloomLevel
15785
- })),
15786
- generatedContentJson: JSON.stringify({ lesson, quiz, activity, slides, codeLab }),
15787
- modelOptions
15788
- });
15789
- semanticScore = judgeReport.totalScore;
15790
- semanticVerdict = judgeReport.overallVerdict;
15791
- for (const criterion of judgeReport.criteria) {
15792
- if (!criterion.passed) {
15793
- allFindings.push({
15794
- id: `llm_judge_${criterion.name.toLowerCase().replace(/\s+/g, "_")}`,
15795
- dimension: "CONSTRUCTIVE_ALIGNMENT",
15796
- severity: criterion.score < 50 ? "CRITICAL" : "MAJOR",
15797
- title: `LLM-as-Judge Finding: ${criterion.name}`,
15798
- description: criterion.feedback,
15799
- remediationAdvice: judgeReport.actionableRepairPrompts.join("; ") || "Refine prompt context."
15800
- });
15801
- } else {
15802
- strengths.push(`[LLM-Judge] ${criterion.name}: ${criterion.feedback}`);
15803
- }
15804
- }
15805
- } catch (err) {
15806
- semanticScore = null;
15807
- semanticVerdict = "FAIL";
15808
- allFindings.push({
15809
- id: "llm_judge_connection_error",
15810
- dimension: "CONSTRUCTIVE_ALIGNMENT",
15811
- severity: "MINOR",
15812
- title: "LLM-as-Judge Skipped / Fallback",
15813
- description: `Semantic inference error: ${err.message || String(err)}`,
15814
- remediationAdvice: "Check API credentials for LLM-as-Judge."
15815
- });
15816
- }
14844
+ const allFindings = [...structuralResult.findings];
14845
+ const strengths = [...structuralResult.strengths];
14846
+ let semanticScore = null;
14847
+ let semanticVerdict = "PASS";
14848
+ if (executeLLMJudge) {
14849
+ try {
14850
+ const judgeReport = await auditCurriculumQualityFlow({
14851
+ targetArtifactType: "LESSON_BUNDLE",
14852
+ lessonId: lesson.lessonId,
14853
+ expectedLanguage: lesson.language,
14854
+ targetObjectives: (lesson.learningObjectives || []).map((lo) => ({
14855
+ code: lo.code,
14856
+ description: lo.description,
14857
+ bloomLevel: lo.bloomLevel
14858
+ })),
14859
+ generatedContentJson: JSON.stringify({ lesson, quiz, activity, slides, codeLab }),
14860
+ modelOptions
14861
+ });
14862
+ semanticScore = judgeReport.totalScore;
14863
+ semanticVerdict = judgeReport.overallVerdict;
14864
+ for (const criterion of judgeReport.criteria) {
14865
+ if (!criterion.passed) {
14866
+ allFindings.push({
14867
+ id: `llm_judge_${criterion.name.toLowerCase().replace(/\s+/g, "_")}`,
14868
+ dimension: "CONSTRUCTIVE_ALIGNMENT",
14869
+ severity: criterion.score < 50 ? "CRITICAL" : "MAJOR",
14870
+ title: `LLM-as-Judge Finding: ${criterion.name}`,
14871
+ description: criterion.feedback,
14872
+ remediationAdvice: judgeReport.actionableRepairPrompts.join("; ") || "Refine prompt context."
14873
+ });
14874
+ } else {
14875
+ strengths.push(`[LLM-Judge] ${criterion.name}: ${criterion.feedback}`);
14876
+ }
14877
+ }
14878
+ } catch (err) {
14879
+ semanticScore = null;
14880
+ semanticVerdict = "FAIL";
14881
+ allFindings.push({
14882
+ id: "llm_judge_connection_error",
14883
+ dimension: "CONSTRUCTIVE_ALIGNMENT",
14884
+ severity: "MINOR",
14885
+ title: "LLM-as-Judge Skipped / Fallback",
14886
+ description: `Semantic inference error: ${err.message || String(err)}`,
14887
+ remediationAdvice: "Check API credentials for LLM-as-Judge."
14888
+ });
14889
+ }
15817
14890
  }
15818
14891
  const criticalCount = allFindings.filter((f) => f.severity === "CRITICAL").length;
15819
14892
  const overallScore = executeLLMJudge && semanticScore !== null ? Math.round(structuralResult.structuralScore * 0.4 + semanticScore * 0.6) : structuralResult.structuralScore;
@@ -16694,32 +15767,316 @@ var MisconceptionEvaluator = class {
16694
15767
  missingExplanation = true;
16695
15768
  }
16696
15769
  }
16697
- if (missingExplanation) {
16698
- score -= 10;
16699
- findings.push({
16700
- id: `misconception_shallow_explanation_${qId}`,
16701
- dimension: "MISCONCEPTION_RIGOR",
16702
- severity: "MAJOR",
16703
- title: `Shallow Distractor Explanations in ${qId}`,
16704
- description: `Question ${qId} lacks detailed pedagogical explanations for all options. Students and teachers cannot diagnose root causes without option-level rationale.`,
16705
- remediationAdvice: "For EVERY option (A, B, C, D), explicitly write why the choice is correct or what mental misconception it represents.",
16706
- affectedElement: qId
15770
+ if (missingExplanation) {
15771
+ score -= 10;
15772
+ findings.push({
15773
+ id: `misconception_shallow_explanation_${qId}`,
15774
+ dimension: "MISCONCEPTION_RIGOR",
15775
+ severity: "MAJOR",
15776
+ title: `Shallow Distractor Explanations in ${qId}`,
15777
+ description: `Question ${qId} lacks detailed pedagogical explanations for all options. Students and teachers cannot diagnose root causes without option-level rationale.`,
15778
+ remediationAdvice: "For EVERY option (A, B, C, D), explicitly write why the choice is correct or what mental misconception it represents.",
15779
+ affectedElement: qId
15780
+ });
15781
+ } else {
15782
+ questionsWithFullExplanations++;
15783
+ }
15784
+ }
15785
+ if (questionsWithFullExplanations === questions.length && questions.length >= 3) {
15786
+ strengths.push("All quiz questions feature rigorous 4-option breakdown with comprehensive misconception analysis.");
15787
+ }
15788
+ score = Math.max(0, Math.min(100, score));
15789
+ return {
15790
+ score,
15791
+ weight: 0.2,
15792
+ passed: score >= 75 && !findings.some((f) => f.severity === "CRITICAL"),
15793
+ strengths,
15794
+ findings
15795
+ };
15796
+ }
15797
+ };
15798
+
15799
+ // src/standards/standardsCoverageGate.ts
15800
+ function resolveStatementRef(ref, packs) {
15801
+ const [head, ...rest] = ref.split(":");
15802
+ const candidatePacks = rest.length > 0 ? packs.filter((p) => p.manifest.id === head) : packs;
15803
+ const statementId = rest.length > 0 ? rest.join(":") : ref;
15804
+ for (const p of candidatePacks) {
15805
+ if (p.statements.some((s) => s.id === statementId)) return { pack: p, statementId };
15806
+ }
15807
+ return null;
15808
+ }
15809
+ function evaluateStandardsCoverage(input) {
15810
+ const rows = [];
15811
+ const aoToLo = /* @__PURE__ */ new Map();
15812
+ for (const ao of input.assessmentObjectives ?? []) aoToLo.set(ao.aoCode, ao.alignedLO);
15813
+ const loToRefs = /* @__PURE__ */ new Map();
15814
+ for (const lo of input.objectives) {
15815
+ loToRefs.set(lo.code, new Set(lo.standardRefs ?? []));
15816
+ }
15817
+ const taughtLOs = /* @__PURE__ */ new Set();
15818
+ for (const act of input.activities ?? []) for (const lo of act.lo) taughtLOs.add(lo);
15819
+ const assessedLOs = /* @__PURE__ */ new Set();
15820
+ for (const q of input.quizQuestions) {
15821
+ if (q.alignedLO) assessedLOs.add(q.alignedLO);
15822
+ if (q.alignedAO) {
15823
+ const lo = aoToLo.get(q.alignedAO);
15824
+ if (lo) assessedLOs.add(lo);
15825
+ }
15826
+ }
15827
+ for (const pack of input.packs) {
15828
+ const mappingByStatement = /* @__PURE__ */ new Map();
15829
+ for (const m of pack.mappings ?? []) {
15830
+ const prev = mappingByStatement.get(m.statementId);
15831
+ if (!prev || prev !== "covers" && m.kind === "covers" || prev === "extends" && m.kind !== "extends") {
15832
+ mappingByStatement.set(m.statementId, m.kind);
15833
+ }
15834
+ }
15835
+ for (const statement of pack.statements) {
15836
+ const refFull = `${pack.manifest.id}:${statement.id}`;
15837
+ const issues = [];
15838
+ const los = input.objectives.filter((lo) => (lo.standardRefs ?? []).some((r) => r === statement.id || r === refFull)).map((lo) => lo.code);
15839
+ const kind = mappingByStatement.get(statement.id);
15840
+ const hasMapping = kind !== void 0;
15841
+ const isComplianceRelevant = kind === "covers";
15842
+ const hasActivity = los.some((lo) => taughtLOs.has(lo));
15843
+ const hasAssessment = los.some((lo) => assessedLOs.has(lo));
15844
+ let status;
15845
+ if (!hasMapping) status = "UNMAPPED";
15846
+ else if (!isComplianceRelevant) status = "PARTIAL";
15847
+ else if (los.length > 0 && hasActivity && hasAssessment) status = "COVERED";
15848
+ else status = "UNCOVERED";
15849
+ if (status === "UNCOVERED") {
15850
+ if (los.length === 0) issues.push("No learning objective anchors this statement (missing standardRefs).");
15851
+ else {
15852
+ if (!hasActivity) issues.push(`Anchored LO(s) ${los.join(", ")} have no activity in lesson flow.`);
15853
+ if (!hasAssessment) issues.push(`Anchored LO(s) ${los.join(", ")} are not measured by any quiz question.`);
15854
+ }
15855
+ }
15856
+ rows.push({
15857
+ packId: pack.manifest.id,
15858
+ statementId: statement.id,
15859
+ statementText: Object.values(statement.texts)[0] ?? "",
15860
+ status,
15861
+ objectives: los,
15862
+ hasActivity,
15863
+ hasAssessment,
15864
+ issues
15865
+ });
15866
+ }
15867
+ }
15868
+ for (const lo of input.objectives) {
15869
+ for (const r of lo.standardRefs ?? []) {
15870
+ if (!resolveStatementRef(r, input.packs)) {
15871
+ rows.push({
15872
+ packId: "(unresolved)",
15873
+ statementId: r,
15874
+ statementText: "",
15875
+ status: "UNCOVERED",
15876
+ objectives: [lo.code],
15877
+ hasActivity: false,
15878
+ hasAssessment: false,
15879
+ issues: [`standardRef "${r}" does not exist in any adopted pack \u2014 likely a hallucinated or stale ID`]
16707
15880
  });
16708
- } else {
16709
- questionsWithFullExplanations++;
16710
15881
  }
16711
15882
  }
16712
- if (questionsWithFullExplanations === questions.length && questions.length >= 3) {
16713
- strengths.push("All quiz questions feature rigorous 4-option breakdown with comprehensive misconception analysis.");
15883
+ }
15884
+ const complianceRows = rows.filter((r) => r.status !== "UNMAPPED" && r.status !== "PARTIAL");
15885
+ const covered = complianceRows.filter((r) => r.status === "COVERED").length;
15886
+ const coveragePct = complianceRows.length === 0 ? 100 : Math.round(covered / complianceRows.length * 100);
15887
+ const unresolvedCount = rows.filter((r) => r.packId === "(unresolved)").length;
15888
+ const verdict = unresolvedCount > 0 || coveragePct < 60 ? "FAIL" : coveragePct < 100 ? "WARN" : "PASS";
15889
+ const lines = [
15890
+ "# Standards Coverage Report",
15891
+ "",
15892
+ `- Verdict: **${verdict}**`,
15893
+ `- Compliance coverage: **${coveragePct}%** (${covered}/${complianceRows.length} "covers" statements fully taught + assessed)`,
15894
+ `- Unresolved standardRefs: ${unresolvedCount}`,
15895
+ "",
15896
+ "| Pack | Statement | Status | LOs | Activity | Assessment |",
15897
+ "|---|---|---|---|---|---|",
15898
+ ...rows.map(
15899
+ (r) => `|${r.packId}|${r.statementId}|${r.status}|${r.objectives.join(", ") || "\u2014"}|${r.hasActivity ? "\u2713" : "\u2717"}|${r.hasAssessment ? "\u2713" : "\u2717"}|`
15900
+ )
15901
+ ];
15902
+ const issueLines = rows.filter((r) => r.issues.length > 0).flatMap((r) => r.issues.map((i) => `- [${r.packId}:${r.statementId}] ${i}`));
15903
+ if (issueLines.length > 0) lines.push("", "## Issues", ...issueLines);
15904
+ return {
15905
+ verdict,
15906
+ coveragePct,
15907
+ rows,
15908
+ summary: `Standards coverage ${coveragePct}% \u2014 verdict ${verdict}`,
15909
+ rawMarkdownReport: lines.join("\n")
15910
+ };
15911
+ }
15912
+ var StandardsRegistryAdapter = class {
15913
+ client;
15914
+ constructor(config = {}) {
15915
+ if (config.client) {
15916
+ this.client = config.client;
15917
+ return;
16714
15918
  }
16715
- score = Math.max(0, Math.min(100, score));
16716
- return {
16717
- score,
16718
- weight: 0.2,
16719
- passed: score >= 75 && !findings.some((f) => f.severity === "CRITICAL"),
16720
- strengths,
16721
- findings
16722
- };
15919
+ const url = config.supabaseUrl || process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL || "";
15920
+ const key = config.supabaseServiceRoleKey || process.env.SUPABASE_SERVICE_ROLE_KEY || config.supabaseAnonKey || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "";
15921
+ if (!url || !key) {
15922
+ throw new Error("StandardsRegistryAdapter: Supabase URL/key missing (pass client or env).");
15923
+ }
15924
+ this.client = createClient(url, key);
15925
+ }
15926
+ // ─── Intake (write path — service role) ───────────────────────────────────
15927
+ /** Persist a schema-validated pack as a new framework (status=draft). */
15928
+ async importPack(pack, opts) {
15929
+ const parsed = FrameworkPackSchema.safeParse(pack);
15930
+ if (!parsed.success) {
15931
+ throw new Error("importPack: pack failed schema validation: " + parsed.error.issues.map((i) => i.path.join(".") + ": " + i.message).join("; "));
15932
+ }
15933
+ const p = parsed.data;
15934
+ const { data: fw, error: fwErr } = await this.client.from("standards_frameworks").insert({
15935
+ pack_id: p.manifest.id,
15936
+ content_version: p.manifest.contentVersion,
15937
+ name: p.manifest.name,
15938
+ spec_version: p.manifest.specVersion,
15939
+ subject: p.manifest.subject,
15940
+ languages: p.manifest.languages,
15941
+ grade_model: p.manifest.gradeModel,
15942
+ provenance: p.manifest.provenance,
15943
+ trust: p.manifest.trust,
15944
+ status: "draft",
15945
+ organization_code: opts?.organizationCode ?? (p.manifest.trust === "verified" ? null : p.manifest.provenance.importedBy?.replace(/^org:/, "") || null),
15946
+ original_file_path: opts?.originalFilePath ?? null,
15947
+ created_by: opts?.createdBy ?? null
15948
+ }).select("id").single();
15949
+ if (fwErr) throw new Error("importPack: framework insert failed: " + fwErr.message);
15950
+ const frameworkId = fw.id;
15951
+ const statementRows = p.statements.map((s) => ({
15952
+ framework_id: frameworkId,
15953
+ statement_id: s.id,
15954
+ parent_statement_id: s.parentId ?? null,
15955
+ grade_min: s.gradeBand[0],
15956
+ grade_max: s.gradeBand[1],
15957
+ texts: s.texts,
15958
+ classifications: s.classifications ?? [],
15959
+ bloom_hint: s.bloomHint ?? null,
15960
+ keywords: s.keywords ?? [],
15961
+ source_ref: s.sourceRef ?? null,
15962
+ provenance: s.provenance
15963
+ }));
15964
+ const { error: stErr, count: stCount } = await this.client.from("standards_statements").insert(statementRows, { count: "exact" });
15965
+ if (stErr) throw new Error("importPack: statement insert failed: " + stErr.message);
15966
+ let mappingCount = 0;
15967
+ if (p.mappings && p.mappings.length > 0) {
15968
+ const mappingRows = p.mappings.map((m) => ({
15969
+ framework_id: frameworkId,
15970
+ statement_id: m.statementId,
15971
+ target_ref: m.targetRef,
15972
+ kind: m.kind,
15973
+ confidence: m.confidence,
15974
+ provenance: m.provenance
15975
+ }));
15976
+ const { error: mpErr, count: mpCount } = await this.client.from("standards_mappings").insert(mappingRows, { count: "exact" });
15977
+ if (mpErr) throw new Error("importPack: mapping insert failed: " + mpErr.message);
15978
+ mappingCount = mpCount ?? mappingRows.length;
15979
+ }
15980
+ return { frameworkDbId: frameworkId, statementsInserted: stCount ?? statementRows.length, mappingsInserted: mappingCount };
15981
+ }
15982
+ /** Activate a draft framework (immutable version is now live). */
15983
+ async activatePack(frameworkDbId) {
15984
+ const { error } = await this.client.from("standards_frameworks").update({ status: "active" }).eq("id", frameworkDbId).eq("status", "draft");
15985
+ if (error) throw new Error("activatePack failed: " + error.message);
15986
+ }
15987
+ async deprecatePack(frameworkDbId) {
15988
+ const { error } = await this.client.from("standards_frameworks").update({ status: "deprecated" }).eq("id", frameworkDbId);
15989
+ if (error) throw new Error("deprecatePack failed: " + error.message);
15990
+ }
15991
+ async adoptPack(frameworkDbId, organizationCode, adoptedBy) {
15992
+ const { error } = await this.client.from("standards_org_adoptions").upsert(
15993
+ { framework_id: frameworkDbId, organization_code: organizationCode, adopted_by: adoptedBy ?? null },
15994
+ { onConflict: "organization_code,framework_id" }
15995
+ );
15996
+ if (error) throw new Error("adoptPack failed: " + error.message);
15997
+ }
15998
+ // ─── Runtime (read path — generation) ─────────────────────────────────────
15999
+ /**
16000
+ * Load all packs usable by an org: platform packs (verified, org IS NULL) +
16001
+ * org packs + org adoptions. Hydrated into the same FrameworkPack shape the
16002
+ * in-memory Phase-1 components consume (injector / coverage gate / judge).
16003
+ */
16004
+ async loadPacksForOrg(organizationCode) {
16005
+ let query = this.client.from("standards_frameworks").select("*, standards_org_adoptions(organization_code)").eq("status", "active");
16006
+ const { data: frameworks, error } = await query;
16007
+ if (error) throw new Error("loadPacksForOrg: " + error.message);
16008
+ const visible = (frameworks ?? []).filter((f) => {
16009
+ const own = f.organization_code && organizationCode && f.organization_code === organizationCode;
16010
+ const global = !f.organization_code;
16011
+ const adopted = organizationCode && (f.standards_org_adoptions ?? []).some((a) => a.organization_code === organizationCode);
16012
+ return global || own || adopted;
16013
+ });
16014
+ if (visible.length === 0) return [];
16015
+ const ids = visible.map((f) => f.id);
16016
+ const [{ data: statements, error: stErr }, { data: mappings, error: mpErr }, { data: overrides, error: ovErr }] = await Promise.all([
16017
+ this.client.from("standards_statements").select("*").in("framework_id", ids),
16018
+ this.client.from("standards_mappings").select("*").in("framework_id", ids),
16019
+ this.client.from("standards_mapping_overrides").select("*").in("framework_id", ids)
16020
+ ]);
16021
+ if (stErr) throw new Error("loadPacksForOrg statements: " + stErr.message);
16022
+ const overridesByFw = /* @__PURE__ */ new Map();
16023
+ for (const o of overrides ?? []) {
16024
+ const list = overridesByFw.get(o.framework_id) ?? [];
16025
+ list.push(o);
16026
+ overridesByFw.set(o.framework_id, list);
16027
+ }
16028
+ return visible.map((f) => {
16029
+ const stmts = (statements ?? []).filter((s) => s.framework_id === f.id).map((s) => ({
16030
+ id: s.statement_id,
16031
+ parentId: s.parent_statement_id ?? void 0,
16032
+ gradeBand: [s.grade_min, s.grade_max],
16033
+ texts: s.texts,
16034
+ classifications: s.classifications ?? [],
16035
+ bloomHint: s.bloom_hint ?? void 0,
16036
+ keywords: s.keywords ?? [],
16037
+ sourceRef: s.source_ref ?? void 0,
16038
+ provenance: s.provenance ?? { method: "imported" }
16039
+ }));
16040
+ const ov = overridesByFw.get(f.id) ?? [];
16041
+ const retired = new Set(ov.filter((o) => o.kind === "retired").map((o) => o.statement_id + "|" + o.target_ref));
16042
+ const embedded = (mappings ?? []).filter((m) => m.framework_id === f.id).map((m) => ({
16043
+ statementId: m.statement_id,
16044
+ targetRef: m.target_ref,
16045
+ kind: m.kind,
16046
+ confidence: Number(m.confidence),
16047
+ provenance: m.provenance ?? { method: "imported" }
16048
+ }));
16049
+ const overlay = ov.filter((o) => o.kind !== "retired").map((o) => ({
16050
+ statementId: o.statement_id,
16051
+ targetRef: o.target_ref,
16052
+ kind: o.kind,
16053
+ confidence: Number(o.confidence),
16054
+ provenance: { method: "human", reviewedBy: o.created_by ?? "overlay", note: o.note ?? void 0 }
16055
+ }));
16056
+ const bridge = /* @__PURE__ */ new Map();
16057
+ for (const m of embedded) bridge.set(m.statementId + "|" + m.targetRef, m);
16058
+ for (const m of overlay) bridge.set(m.statementId + "|" + m.targetRef, m);
16059
+ const finalMappings = [...bridge.values()].filter((m) => !retired.has(m.statementId + "|" + m.targetRef));
16060
+ const hydrated = FrameworkPackSchema.safeParse({
16061
+ manifest: {
16062
+ id: f.pack_id,
16063
+ name: f.name,
16064
+ specVersion: f.spec_version || "1.0",
16065
+ contentVersion: f.content_version,
16066
+ subject: f.subject,
16067
+ languages: f.languages,
16068
+ gradeModel: f.grade_model,
16069
+ provenance: f.provenance,
16070
+ trust: f.trust
16071
+ },
16072
+ statements: stmts,
16073
+ mappings: finalMappings
16074
+ });
16075
+ if (!hydrated.success) {
16076
+ throw new Error('loadPacksForOrg: hydrated pack "' + f.pack_id + '" failed schema: ' + hydrated.error.issues[0]?.message);
16077
+ }
16078
+ return { ...hydrated.data, dbId: f.id, status: f.status, organizationCode: f.organization_code ?? null };
16079
+ });
16723
16080
  }
16724
16081
  };
16725
16082
 
@@ -17210,6 +16567,6 @@ function renderMediaPlaceholder(entry) {
17210
16567
  return `> \u{1F5BC}\uFE0F [\u1EA2nh \u0111ang ch\u1EDD: ${entry.name} \u2014 s\u1EBD \u0111\u01B0\u1EE3c t\u1EA1o sau khi duy\u1EC7t media]`;
17211
16568
  }
17212
16569
 
17213
- export { ACT_TEMPLATE, ARTIFACT_EXECUTION_ORDER, AcademicAuditor, ActiveLearningWorkflowSchema, ActivityLabSchema, ActivitySeqRowSchema, ActivityStepSchema, ArtifactContractRowSchema, AssessmentMapRowSchema, AssessmentObjectiveSchema, BELL_RINGER_TEMPLATE, BLOOM_ACTION_VERBS, BOM_TEMPLATE, BUNDLED_STANDARDS_PACK_IDS, BellRingerDaySchema, BellRingerSchema, BloomHintSchema, BloomLevelSchema, BloomTaxonomyEvaluator, BronzeTierSchema, CERConclusionSchema, CHOICE_BOARD_TEMPLATE, CODE_LAB_TEMPLATE, CURRICULUM_SLASH_COMMANDS, CardTierEnum, ChoiceBoardRubricRowSchema, ChoiceBoardSchema, ChoiceRuleSchema, ChoiceSquareSchema, ClassificationSchema, CodeHardwareFeasibilityEvaluator, CodeLabSchema, CodeSnippetSchema, CompetencyRubricRowSchema, ComputationalThinkingSchema, ConstructiveAlignmentEvaluator, CoreConceptSchema, CourseObjectiveSchema, CoverageReportSchema, CrossArtifactDriftEvaluator, CurriculumArtifactSchema, CurriculumError, CurriculumPlanSchema, CurriculumQualityReportSchema, DEFAULT_ENABLED_PROVIDERS, DEFAULT_GATE_SETTINGS, DEFAULT_STREAM_IDLE_MS, DEFAULT_STREAM_TOTAL_MS, DependencyEdgeSchema, DepthAssignmentSchema, DepthLevelSchema, DeterministicPipelineRunner, DeterministicStructuralLinter, DiagnosticQuestionSchema, DiagnosticQuizSchema, EDPPhaseEnum, EXIT_TICKET_TEMPLATE, EXPOSITION_REL, EXT_TEMPLATE, EntryLevelSchema, ExitTicketBugHuntSchema, ExitTicketMetacognitionSchema, ExitTicketQuickItemSchema, ExitTicketRecallPromptSchema, ExitTicketSchema, ExperimentalDesignSchema, ExpositionApprovalError, ExtensionChallengeSchema, ExtensionSchema, FileSystemCurriculumAdapter, FiveEFlowPhaseSchema, FiveEInstructionalEvaluator, FiveEPhaseEnum, FrameworkMappingSchema, FrameworkPackManifestSchema, FrameworkPackSchema, FrameworkStatementSchema, GLOSSARY_TEMPLATE, GRAPHIC_ORGANIZER_TEMPLATE, GlossaryQuickRefRowSchema, GlossarySchema, GlossaryTermSchema, GoldTierSchema, GradeBandSchema, GraphicOrganizerItemSchema, GraphicOrganizerSchema, HANDOUT_TEMPLATE, HandoutSchema, HandoutSectionSchema, InstructionSectionSchema, InstructionStepSchema, JudgeCriterionSchema, LAYER_TOTAL_BUDGET_MS, LESSON_PLAN_TEMPLATE, LLMJudgeEngine, LabTierTaskSchema, LearningObjectiveInputSchema, LearningObjectiveRowSchema, LessonFlowPhaseSchema, LessonPlan5ESchema, LessonPlanEDPSchema, LessonPlanSchema, LessonSectionSchema, LocalWorkspaceManager, MappingKindSchema, MarpSlideSchema, MediaError, MediaLedger, MentorCheatsheetSchema, MilestoneInputSchema, MisconceptionEvaluator, MisconceptionSchema, ModelPolicyResolver, NVIDIA_MODELS, NodeKindSchema, OrganizerTypeEnum, PROJECT_INSTRUCTION_TEMPLATE, PhaseInputSchema, PlanConstraintsSchema, PlannerInputSchema, PlanningGraphSchema, PlanningNodeSchema, PrerequisiteDecisionEntrySchema, PrerequisiteDecisionSchema, ProductGoalSchema, ProgressiveHintsSchema, ProjectGraphSchema, ProjectInstructionSchema, ProjectProfileSchema, QUIZ_TEMPLATE, QualityAuditVerdictSchema, QuizOptionSchema, REVIEW_GAME_TEMPLATE, RUBRIC_TEMPLATE, RecordProvenanceSchema, ResourceMapRowSchema, ReviewGameExportRowSchema, ReviewGameQuestionSchema, ReviewGameSchema, RoadmapInputSchema, RotationStationSchema, RubricCriteriaSchema, RubricSchema, SCIENCE_LAB_TEMPLATE, SELF_LAB_TEMPLATE, SLIDE_LAYOUT_PRESETS, SLIDE_TEMPLATE, STANDARD_SOT_FILES, STATION_ROTATION_TEMPLATE, ScaffoldDecisionEntrySchema, ScaffoldDecisionSchema, ScienceLabSchema, ScoringRubricRowSchema, SelfLabSchema, SelfPacedBundleSchema, SelfPacedChallengeSchema, SessionPlanSchema, SilverTierSchema, SlideDeckSchema, StandardizedTermRowSchema, StandardsRegistryAdapter, StationRecordRowSchema, StationRotationSchema, StationTypeEnum, SupabaseCurriculumAdapter, TASK_CARDS_TEMPLATE, TEACHER_GUIDE_TEMPLATE, TIERED_PRACTICE_TEMPLATE, TaskCardSchema, TaskCardsSchema, TeacherGuideSchema, TeachingScriptPhaseSchema, TierLevelEnum, TierObjectiveSchema, TieredPracticeSchema, TranslationPolicySchema, TroubleshootingEntrySchema, TroubleshootingRowSchema, UnitPlanSchema, UserJourneySchema, WORKSHEET_TEMPLATE, WorksheetItemSchema, WorksheetItemTypeEnum, WorksheetPartSchema, WorksheetSchema, activityTools, analystTools, analyzeProjectCreationIntent, approvalHookToken, approvalPayloadSchema, assertAcyclic, assessorTools, auditCurriculumQualityFlow, auditQualityReport, buildActivityPrompt, buildCodeLabPrompt, buildCurriculumContext, buildCurriculumPlan, buildDeliveryPackages, buildDiagnosticQuizPrompt, buildExpositionContext, buildExtensionPrompt, buildHandoutPrompt, buildImagePrompt, buildJudgePrompt, buildLessonMasterPrompt, buildProjectInstructionPrompt, buildSelfLabPrompt, buildSessionSliceContext, buildSlidesPrompt, buildStandardStackMarkdown, buildStandardsContext, buildStandardsContextBlock, buildTeacherGuidePrompt, buildWorksheetPrompt, computeContentHash, computePackingSpec, conductPreliminaryResearch, contentTools, convertRoadmapToFoundationSot, createAiInferenceError, createCurriculumStorage, createStreamAbortSignal, curateMediaLedger, designerTools, detectProjectPedagogy, ensureExpositionForLesson, ensureKnowledgeExposition, evaluateStandardsCoverage, executeCurriculumCommand, executeSingleArtifactStep, exportAllProjectQuizzes, expositionCacheKey, extractScopeSequenceRows, extractSessionSlice, extractStreamChunk, extractThoughtAndContent, formatQuizzesToCsv, fulfillMediaLedger, gateModeFor, generateActivityFlow, generateActivityStep, generateCodeLabFlow, generateCodeLabStep, generateDiagnosticQuizFlow, generateDiagnosticQuizStep, generateEducationalImage, generateExtensionFlow, generateExtensionStep, generateHandoutFlow, generateHandoutStep, generateLessonMasterFlow, generateMasterLessonStep, generateMilestoneCurriculumBundle, generateMilestoneWorkflow, generatePhase1SotArtifacts, generatePhase2SotArtifacts, generateProjectInstruction, generateProjectInstructionFlow, generateRoadmapCurriculum, generateRoadmapWorkflow, generateSelfLabFlow, generateSelfLabStep, generateSelfPacedBundle, generateSingleArtifact, generateSingleArtifactWorkflow, generateSlidesFlow, generateSlidesStep, generateTeacherGuideFlow, generateTeacherGuideStep, generateWorksheetFlow, generateWorksheetStep, getAIModel, getArtifactMetadata, getDesignatedFallbackChain, getDesignatedFallbackConfig, getLessonMetadata, getProjectArtifactScope, getProjectStatusReport, getSlideLayoutPresetById, getSlideLayoutPresetsByCategory, getSmartResumeContext, glossaryTermsForSession, illustratorTools, importRoadmapToProject, ingestProjectGraphIntoProject, initializeProjectInStorage, injectMediaIntoMarkdown, isExpositionFresh, isModelAllowed, isProviderEnabled, isTranslationDue, judgeMasterLessonStep, judgeSatelliteStep, lessonApprovalHook, lintAndSanitizeArtifact, lintCurriculumFramework, lintFrameworkPack, loadCurriculumTemplate, loadSotTemplate, normalizePlanningGraph, packagerTools, parseGateSettings, parseQuizMarkdown, parseRoadmapJsonToProjectPayload, produceBatchLessons, produceSingleLesson, publishToGitHub, publishToGitStep, publishToSupabase, publishToSupabaseStep, rankGenCandidates, renderFrameworkFromPlan, renderMediaPlaceholder, repairMasterLessonStep, researcherTools, resolveGateSettings, resolveStandardsPacks, resolveStreamBudget, resolveTranslationTargets, reviewerTools, runCurriculumAIInference, safeParseJson, saveMilestoneToWorkspaceStep, searchEducationalImages, searchEducationalVideos, selectStatementsForLesson, serializeActivityToMarkdown, serializeCodeLabToMarkdown, serializeDiagnosticQuizToMarkdown, serializeHandoutToMarkdown, serializeLessonToMarkdown, serializeProjectInstructionToMarkdown, serializeSelfLabToMarkdown, slugifyProjectName, streamCurriculumAIInference, streamCurriculumAIInferenceWithTools, streamDiagnosticQuizFlow, streamLLMWithFallback, streamLayerPrefillWithFallback, streamLessonMasterFlow, streamSelfLabFlow, techSmeTools, topoSort, uploadAssetToBucket, validateCurriculumPlan, validateFrameworkPack, validateMarkdownTables, validateMermaidSyntax, withAutoRepair, withRateLimitBackoff };
16570
+ export { ACT_TEMPLATE, ARTIFACT_EXECUTION_ORDER, AcademicAuditor, ActiveLearningWorkflowSchema, ActivityLabSchema, ActivitySeqRowSchema, ActivityStepSchema, ArtifactContractRowSchema, AssessmentMapRowSchema, AssessmentObjectiveSchema, BELL_RINGER_TEMPLATE, BLOOM_ACTION_VERBS, BOM_TEMPLATE, BUNDLED_STANDARDS_PACK_IDS, BellRingerDaySchema, BellRingerSchema, BloomHintSchema, BloomLevelSchema, BloomTaxonomyEvaluator, BronzeTierSchema, CERConclusionSchema, CHOICE_BOARD_TEMPLATE, CODE_LAB_TEMPLATE, CURRICULUM_SLASH_COMMANDS, CardTierEnum, ChoiceBoardRubricRowSchema, ChoiceBoardSchema, ChoiceRuleSchema, ChoiceSquareSchema, ClassificationSchema, CodeHardwareFeasibilityEvaluator, CodeLabSchema, CodeSnippetSchema, CompetencyRubricRowSchema, ComputationalThinkingSchema, ConstructiveAlignmentEvaluator, CoreConceptSchema, CourseObjectiveSchema, CoverageReportSchema, CrossArtifactDriftEvaluator, CurriculumArtifactSchema, CurriculumError, CurriculumPlanSchema, CurriculumQualityReportSchema, DEFAULT_ENABLED_PROVIDERS, DEFAULT_GATE_SETTINGS, DEFAULT_STREAM_IDLE_MS, DEFAULT_STREAM_TOTAL_MS, DependencyEdgeSchema, DepthAssignmentSchema, DepthLevelSchema, DeterministicPipelineRunner, DeterministicStructuralLinter, DiagnosticQuestionSchema, DiagnosticQuizSchema, EDPPhaseEnum, EXIT_TICKET_TEMPLATE, EXPOSITION_REL, EXT_TEMPLATE, EntryLevelSchema, ExitTicketBugHuntSchema, ExitTicketMetacognitionSchema, ExitTicketQuickItemSchema, ExitTicketRecallPromptSchema, ExitTicketSchema, ExperimentalDesignSchema, ExpositionApprovalError, ExtensionChallengeSchema, ExtensionSchema, FileSystemCurriculumAdapter, FiveEFlowPhaseSchema, FiveEInstructionalEvaluator, FiveEPhaseEnum, FrameworkMappingSchema, FrameworkPackManifestSchema, FrameworkPackSchema, FrameworkStatementSchema, GLOSSARY_TEMPLATE, GRAPHIC_ORGANIZER_TEMPLATE, GlossaryQuickRefRowSchema, GlossarySchema, GlossaryTermSchema, GoldTierSchema, GradeBandSchema, GraphicOrganizerItemSchema, GraphicOrganizerSchema, HANDOUT_TEMPLATE, HandoutSchema, HandoutSectionSchema, InstructionSectionSchema, InstructionStepSchema, JudgeCriterionSchema, LAYER_TOTAL_BUDGET_MS, LESSON_PLAN_TEMPLATE, LLMJudgeEngine, LabTierTaskSchema, LearningObjectiveInputSchema, LearningObjectiveRowSchema, LessonFlowPhaseSchema, LessonPlan5ESchema, LessonPlanEDPSchema, LessonPlanSchema, LessonSectionSchema, LocalWorkspaceManager, MappingKindSchema, MarpSlideSchema, MediaError, MediaLedger, MentorCheatsheetSchema, MilestoneInputSchema, MisconceptionEvaluator, MisconceptionSchema, ModelPolicyResolver, NVIDIA_MODELS, NodeKindSchema, OrganizerTypeEnum, PROJECT_INSTRUCTION_TEMPLATE, PhaseInputSchema, PlanConstraintsSchema, PlannerInputSchema, PlanningGraphSchema, PlanningNodeSchema, PrerequisiteDecisionEntrySchema, PrerequisiteDecisionSchema, ProductGoalSchema, ProgressiveHintsSchema, ProjectGraphSchema, ProjectInstructionSchema, ProjectProfileSchema, QUIZ_TEMPLATE, QualityAuditVerdictSchema, QuizOptionSchema, REVIEW_GAME_TEMPLATE, RUBRIC_TEMPLATE, RecordProvenanceSchema, ResourceMapRowSchema, ReviewGameExportRowSchema, ReviewGameQuestionSchema, ReviewGameSchema, RoadmapInputSchema, RotationStationSchema, RubricCriteriaSchema, RubricSchema, SCIENCE_LAB_TEMPLATE, SELF_LAB_TEMPLATE, SLIDE_LAYOUT_PRESETS, SLIDE_TEMPLATE, STANDARD_SOT_FILES, STATION_ROTATION_TEMPLATE, ScaffoldDecisionEntrySchema, ScaffoldDecisionSchema, ScienceLabSchema, ScoringRubricRowSchema, SelfLabSchema, SelfPacedBundleSchema, SelfPacedChallengeSchema, SessionPlanSchema, SilverTierSchema, SlideDeckSchema, StandardizedTermRowSchema, StandardsRegistryAdapter, StationRecordRowSchema, StationRotationSchema, StationTypeEnum, SupabaseCurriculumAdapter, TASK_CARDS_TEMPLATE, TEACHER_GUIDE_TEMPLATE, TIERED_PRACTICE_TEMPLATE, TaskCardSchema, TaskCardsSchema, TeacherGuideSchema, TeachingScriptPhaseSchema, TierLevelEnum, TierObjectiveSchema, TieredPracticeSchema, TranslationPolicySchema, TroubleshootingEntrySchema, TroubleshootingRowSchema, UnitPlanSchema, UserJourneySchema, WORKSHEET_TEMPLATE, WorksheetItemSchema, WorksheetItemTypeEnum, WorksheetPartSchema, WorksheetSchema, activityTools, analystTools, analyzeProjectCreationIntent, assertAcyclic, assessorTools, auditCurriculumQualityFlow, auditQualityReport, buildActivityPrompt, buildCodeLabPrompt, buildCurriculumContext, buildCurriculumPlan, buildDeliveryPackages, buildDiagnosticQuizPrompt, buildExpositionContext, buildExtensionPrompt, buildHandoutPrompt, buildImagePrompt, buildJudgePrompt, buildLessonMasterPrompt, buildProjectInstructionPrompt, buildSelfLabPrompt, buildSessionSliceContext, buildSlidesPrompt, buildStandardStackMarkdown, buildStandardsContext, buildStandardsContextBlock, buildTeacherGuidePrompt, buildWorksheetPrompt, computeContentHash, computePackingSpec, conductPreliminaryResearch, contentTools, convertRoadmapToFoundationSot, createAiInferenceError, createCurriculumStorage, createStreamAbortSignal, curateMediaLedger, designerTools, detectProjectPedagogy, ensureExpositionForLesson, ensureKnowledgeExposition, evaluateStandardsCoverage, executeCurriculumCommand, exportAllProjectQuizzes, expositionCacheKey, extractScopeSequenceRows, extractSessionSlice, extractStreamChunk, extractThoughtAndContent, formatQuizzesToCsv, fulfillMediaLedger, gateModeFor, generateActivityFlow, generateCodeLabFlow, generateDiagnosticQuizFlow, generateEducationalImage, generateExtensionFlow, generateHandoutFlow, generateLessonMasterFlow, generateMilestoneCurriculumBundle, generatePhase1SotArtifacts, generatePhase2SotArtifacts, generateProjectInstruction, generateProjectInstructionFlow, generateRoadmapCurriculum, generateSelfLabFlow, generateSelfPacedBundle, generateSingleArtifact, generateSlidesFlow, generateTeacherGuideFlow, generateWorksheetFlow, getAIModel, getArtifactMetadata, getDesignatedFallbackChain, getDesignatedFallbackConfig, getLessonMetadata, getProjectArtifactScope, getProjectStatusReport, getSlideLayoutPresetById, getSlideLayoutPresetsByCategory, getSmartResumeContext, glossaryTermsForSession, illustratorTools, importRoadmapToProject, ingestProjectGraphIntoProject, initializeProjectInStorage, injectMediaIntoMarkdown, isExpositionFresh, isModelAllowed, isProviderEnabled, isTranslationDue, lintAndSanitizeArtifact, lintCurriculumFramework, lintFrameworkPack, loadCurriculumTemplate, loadSotTemplate, normalizePlanningGraph, packagerTools, parseGateSettings, parseQuizMarkdown, parseRoadmapJsonToProjectPayload, produceBatchLessons, produceSingleLesson, publishToGitHub, publishToSupabase, rankGenCandidates, renderFrameworkFromPlan, renderMediaPlaceholder, researcherTools, resolveGateSettings, resolveStandardsPacks, resolveStreamBudget, resolveTranslationTargets, reviewerTools, runCurriculumAIInference, safeParseJson, searchEducationalImages, searchEducationalVideos, selectStatementsForLesson, serializeActivityToMarkdown, serializeCodeLabToMarkdown, serializeDiagnosticQuizToMarkdown, serializeHandoutToMarkdown, serializeLessonToMarkdown, serializeProjectInstructionToMarkdown, serializeSelfLabToMarkdown, slugifyProjectName, streamCurriculumAIInference, streamCurriculumAIInferenceWithTools, streamDiagnosticQuizFlow, streamLLMWithFallback, streamLayerPrefillWithFallback, streamLessonMasterFlow, streamSelfLabFlow, techSmeTools, topoSort, uploadAssetToBucket, validateCurriculumPlan, validateFrameworkPack, validateMarkdownTables, validateMermaidSyntax, withAutoRepair };
17214
16571
  //# sourceMappingURL=index.mjs.map
17215
16572
  //# sourceMappingURL=index.mjs.map