@thanh01.pmt/curriculum-kit 1.4.35 → 1.4.37

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.
Files changed (35) hide show
  1. package/dist/ai/index.cjs +57 -1
  2. package/dist/ai/index.cjs.map +1 -1
  3. package/dist/ai/index.d.cts +2 -2
  4. package/dist/ai/index.d.ts +2 -2
  5. package/dist/ai/index.mjs +57 -1
  6. package/dist/ai/index.mjs.map +1 -1
  7. package/dist/{gateSettings-DabOqP6_.d.cts → gateSettings-oo5tCUS_.d.cts} +18 -0
  8. package/dist/{gateSettings-DabOqP6_.d.ts → gateSettings-oo5tCUS_.d.ts} +18 -0
  9. package/dist/index.cjs +793 -51
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +251 -74
  12. package/dist/index.d.ts +251 -74
  13. package/dist/index.mjs +775 -52
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/pipeline/index.cjs +57 -1
  16. package/dist/pipeline/index.cjs.map +1 -1
  17. package/dist/pipeline/index.mjs +57 -1
  18. package/dist/pipeline/index.mjs.map +1 -1
  19. package/dist/schemas/index.cjs +63 -1
  20. package/dist/schemas/index.cjs.map +1 -1
  21. package/dist/schemas/index.d.cts +436 -33
  22. package/dist/schemas/index.d.ts +436 -33
  23. package/dist/schemas/index.mjs +58 -2
  24. package/dist/schemas/index.mjs.map +1 -1
  25. package/dist/standards/index.d.cts +2 -2
  26. package/dist/standards/index.d.ts +2 -2
  27. package/dist/workflow/index.cjs +84 -2
  28. package/dist/workflow/index.cjs.map +1 -1
  29. package/dist/workflow/index.d.cts +3 -3
  30. package/dist/workflow/index.d.ts +3 -3
  31. package/dist/workflow/index.mjs +84 -2
  32. package/dist/workflow/index.mjs.map +1 -1
  33. package/package.json +1 -1
  34. package/dist/{standardsCoverageGate-DR5YTtlt.d.cts → standardsCoverageGate-49pJq5t8.d.cts} +6 -6
  35. package/dist/{standardsCoverageGate-DR5YTtlt.d.ts → standardsCoverageGate-49pJq5t8.d.ts} +6 -6
package/dist/index.cjs CHANGED
@@ -3574,6 +3574,50 @@ var ScaffoldDecisionEntrySchema = zod.z.object({
3574
3574
  minutes_saved: zod.z.number().int().nonnegative().default(0),
3575
3575
  reason: zod.z.string().min(3)
3576
3576
  });
3577
+ var ZpdVerdictSchema = zod.z.enum(["OK", "TOO_MANY_NEW", "NO_ZPD_BRIDGE"]);
3578
+ var SessionZpdStatusSchema = zod.z.object({
3579
+ verdict: ZpdVerdictSchema,
3580
+ new_concept_count: zod.z.number().int().nonnegative().default(0),
3581
+ known_concept_count: zod.z.number().int().nonnegative().default(0),
3582
+ issues: zod.z.array(zod.z.string()).default([])
3583
+ });
3584
+ var ConceptSpiralEncounterSchema = zod.z.object({
3585
+ concept_code: zod.z.string(),
3586
+ session_id: LessonCodeSchema,
3587
+ encounter_index: zod.z.number().int().positive(),
3588
+ bloom_cap: BloomLevelSchema,
3589
+ depth: DepthLevelSchema
3590
+ });
3591
+ var WalkingSkeletonSchema = zod.z.object({
3592
+ epitome_unit_id: zod.z.string().default("U01"),
3593
+ epitome_deliverables: zod.z.array(zod.z.string()).default([]),
3594
+ elaborations: zod.z.array(zod.z.object({
3595
+ unit_id: zod.z.string(),
3596
+ focus_area: zod.z.string(),
3597
+ elaborates_on: zod.z.array(zod.z.string()).default([])
3598
+ })).default([])
3599
+ });
3600
+ var MasteryGateSchema = zod.z.object({
3601
+ phase_or_unit_id: zod.z.string(),
3602
+ gate_name: zod.z.string(),
3603
+ exit_criteria: zod.z.array(zod.z.string()).min(1),
3604
+ next_unit_id: zod.z.string(),
3605
+ remediation: zod.z.object({
3606
+ trigger: zod.z.literal("FAIL_GATE_CRITERIA"),
3607
+ focus_minutes: zod.z.number().int().default(15),
3608
+ action: zod.z.string()
3609
+ })
3610
+ });
3611
+ var ConceptPrerequisiteEdgeSchema = zod.z.object({
3612
+ concept_code: zod.z.string(),
3613
+ requires: zod.z.string(),
3614
+ source: zod.z.enum(["MASTER_TREE", "INFERRED", "CONCEPT_LEVEL_PREREQ"]).default("CONCEPT_LEVEL_PREREQ"),
3615
+ confidence: zod.z.number().min(0).max(1).default(0.8),
3616
+ rationale: zod.z.string().default(""),
3617
+ structure_supported: zod.z.boolean().default(true),
3618
+ needs_review: zod.z.boolean().default(false),
3619
+ hub: zod.z.boolean().default(false)
3620
+ });
3577
3621
  var SessionPlanSchema = zod.z.object({
3578
3622
  id: LessonCodeSchema,
3579
3623
  unit_id: zod.z.string().regex(/^U\d{2}$/),
@@ -3594,7 +3638,14 @@ var SessionPlanSchema = zod.z.object({
3594
3638
  scaffold_decisions: zod.z.array(ScaffoldDecisionEntrySchema).default([]),
3595
3639
  // What the student can demonstrate after this session — feeds exit tickets.
3596
3640
  exit_evidence: zod.z.array(zod.z.string()).min(1),
3597
- differentiation: zod.z.object({ bronze: zod.z.string(), silver: zod.z.string(), gold: zod.z.string() })
3641
+ differentiation: zod.z.object({ bronze: zod.z.string(), silver: zod.z.string(), gold: zod.z.string() }),
3642
+ // P50: ZPD cognitive load status for this session
3643
+ zpd_status: SessionZpdStatusSchema.default({
3644
+ verdict: "OK",
3645
+ new_concept_count: 0,
3646
+ known_concept_count: 0,
3647
+ issues: []
3648
+ })
3598
3649
  });
3599
3650
  var TranslationPolicySchema = zod.z.object({
3600
3651
  policy: zod.z.enum(["after_artifact", "end_of_unit", "at_publish", "native"]).default("native"),
@@ -3649,6 +3700,11 @@ var CurriculumPlanSchema = zod.z.object({
3649
3700
  // [2]
3650
3701
  sessions: zod.z.array(SessionPlanSchema).min(1),
3651
3702
  // [3][4][5][6]
3703
+ // P50: Macro-pedagogical course structures
3704
+ walking_skeleton: WalkingSkeletonSchema.optional(),
3705
+ mastery_gates: zod.z.array(MasteryGateSchema).default([]),
3706
+ concept_spiral_progression: zod.z.array(ConceptSpiralEncounterSchema).default([]),
3707
+ concept_prerequisites: zod.z.array(ConceptPrerequisiteEdgeSchema).default([]),
3652
3708
  glossary_scope: zod.z.array(zod.z.object({
3653
3709
  session_id: LessonCodeSchema,
3654
3710
  terms: zod.z.array(zod.z.string())
@@ -11279,7 +11335,8 @@ function parseAllSessions(plan, frameworkMarkdown) {
11279
11335
  prose_objective: s.prose_objective || s.objective || "",
11280
11336
  new_keywords: Array.isArray(s.new_keywords) ? s.new_keywords : Array.isArray(s.keywords) ? s.keywords : [],
11281
11337
  prerequisite_keywords: Array.isArray(s.prerequisite_keywords) ? s.prerequisite_keywords : [],
11282
- depth_assignments: Array.isArray(s.depth_assignments) ? s.depth_assignments : []
11338
+ depth_assignments: Array.isArray(s.depth_assignments) ? s.depth_assignments : [],
11339
+ zpd_status: s.zpd_status
11283
11340
  }));
11284
11341
  }
11285
11342
  }
@@ -11427,12 +11484,16 @@ async function extractCurriculumHorizon(opts) {
11427
11484
  title: s.title,
11428
11485
  keywords: s.new_keywords || []
11429
11486
  }));
11487
+ const rawSpirals = Array.isArray(plan?.concept_spiral_progression) ? plan.concept_spiral_progression : [];
11488
+ const sessionSpirals = rawSpirals.filter((e) => e.session_id === targetLessonId);
11430
11489
  return {
11431
11490
  targetLessonId,
11432
11491
  targetLessonIndex: targetIndex + 1,
11433
11492
  totalLessons: sessions.length,
11434
11493
  targetTitle: currentSession.title,
11435
11494
  targetKeywords: currentSession.new_keywords || [],
11495
+ zpdStatus: currentSession.zpd_status,
11496
+ spiralProgression: sessionSpirals,
11436
11497
  compactMasterySet: {
11437
11498
  masteredKeywords,
11438
11499
  masteredConcepts
@@ -11448,6 +11509,8 @@ function renderHorizonPromptBlock(horizon) {
11448
11509
  totalLessons,
11449
11510
  targetTitle,
11450
11511
  targetKeywords,
11512
+ zpdStatus,
11513
+ spiralProgression,
11451
11514
  compactMasterySet,
11452
11515
  detailedBridge,
11453
11516
  boundaryPeek
@@ -11455,6 +11518,19 @@ function renderHorizonPromptBlock(horizon) {
11455
11518
  const lines = [
11456
11519
  `# \u{1F9ED} CURRICULUM HORIZON & SCAFFOLDING MATRIX (Lesson ${targetLessonIndex}/${totalLessons}: "${targetTitle}" [${targetLessonId}])`
11457
11520
  ];
11521
+ if (zpdStatus && zpdStatus.verdict !== "OK") {
11522
+ lines.push(
11523
+ `
11524
+ > \u26A0\uFE0F **ZPD COGNITIVE LOAD ALERT (${zpdStatus.verdict}):** ${zpdStatus.issues?.join("; ") || "Scaffolding bridge mandated"}. Introduce minimal new syntax and anchor into known concepts.`
11525
+ );
11526
+ }
11527
+ if (spiralProgression && spiralProgression.length > 0) {
11528
+ const spiralNotes = spiralProgression.map(
11529
+ (sp) => `\`${sp.concept_code}\` (Encounter #${sp.encounter_index}): Bloom Cap [${sp.bloom_cap}], Depth [${sp.depth.toUpperCase()}]`
11530
+ );
11531
+ lines.push(`
11532
+ - **\u{1F300} Bruner Spiral Curricula Guidance:** ${spiralNotes.join(" | ")}`);
11533
+ }
11458
11534
  if (compactMasterySet.masteredKeywords.length > 0 || compactMasterySet.masteredConcepts.length > 0) {
11459
11535
  const rawKeywords = compactMasterySet.masteredKeywords;
11460
11536
  const displayedKeywords = rawKeywords.length > 20 ? rawKeywords.slice(-20) : rawKeywords;
@@ -11625,9 +11701,9 @@ function buildSectionAwareExcerpt(markdown, opts = {}) {
11625
11701
  if (s.canonicalKey && !byCanonical.has(s.canonicalKey)) {
11626
11702
  byCanonical.set(s.canonicalKey, s);
11627
11703
  }
11628
- const norm = normalizeHeading(s.cleanTitle);
11629
- if (!byCleanTitle.has(norm)) {
11630
- byCleanTitle.set(norm, s);
11704
+ const norm2 = normalizeHeading(s.cleanTitle);
11705
+ if (!byCleanTitle.has(norm2)) {
11706
+ byCleanTitle.set(norm2, s);
11631
11707
  }
11632
11708
  if (!s.canonicalKey) {
11633
11709
  unmatched.push(s);
@@ -11765,8 +11841,8 @@ function parseMarkdownSections(markdown, slcMap, artifactType) {
11765
11841
  const flush = () => {
11766
11842
  if (!currentHeading) return;
11767
11843
  const cleanTitle = currentHeading.replace(/^#{1,4}\s+/, "").trim();
11768
- const norm = normalizeHeading(cleanTitle);
11769
- const canonicalKey = reverseMap.get(norm) || findCanonicalFuzzy(norm);
11844
+ const norm2 = normalizeHeading(cleanTitle);
11845
+ const canonicalKey = reverseMap.get(norm2) || findCanonicalFuzzy(norm2);
11770
11846
  sections.push({
11771
11847
  rawHeading: currentHeading,
11772
11848
  cleanTitle,
@@ -11792,49 +11868,55 @@ function parseMarkdownSections(markdown, slcMap, artifactType) {
11792
11868
  function normalizeHeading(str) {
11793
11869
  return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
11794
11870
  }
11795
- function findCanonicalFuzzy(norm) {
11796
- if (norm.includes("toolchain") || norm.includes("moi truong phat trien") || norm.includes("cong cu") || norm.includes("version") || norm.includes("phan mem") || norm.includes("development environment") || norm.includes("technical overview") || norm.includes("kien truc")) {
11871
+ function findCanonicalFuzzy(norm2) {
11872
+ if (norm2.includes("toolchain") || norm2.includes("moi truong phat trien") || norm2.includes("cong cu") || norm2.includes("version") || norm2.includes("phan mem") || norm2.includes("development environment") || norm2.includes("technical overview") || norm2.includes("kien truc")) {
11797
11873
  return "Technical Overview & Architecture Blueprint";
11798
11874
  }
11799
- if (norm.includes("pinout") || norm.includes("phan cung") || norm.includes("hardware") || norm.includes("wiring") || norm.includes("ket noi")) {
11875
+ if (norm2.includes("pinout") || norm2.includes("phan cung") || norm2.includes("hardware") || norm2.includes("wiring") || norm2.includes("ket noi")) {
11800
11876
  return "Hardware Pinout & Wiring Configuration Matrix";
11801
11877
  }
11802
- if (norm.includes("pedagog") || norm.includes("phuong phap") || norm.includes("day hoc") || norm.includes("su pham")) {
11878
+ if (norm2.includes("pedagog") || norm2.includes("phuong phap") || norm2.includes("day hoc") || norm2.includes("su pham")) {
11803
11879
  return "Core Pedagogical Concept Anchor & Real-World Domain Bridge";
11804
11880
  }
11805
- if (norm.includes("standard") || norm.includes("tieu chuan") || norm.includes("csta") || norm.includes("cs2023") || norm.includes("chuan academic")) {
11881
+ if (norm2.includes("standard") || norm2.includes("tieu chuan") || norm2.includes("csta") || norm2.includes("cs2023") || norm2.includes("chuan academic")) {
11806
11882
  return "Standards Alignment";
11807
11883
  }
11808
- if (norm.includes("roadmap") || norm.includes("lo trinh") || norm.includes("milestone") || norm.includes("giai doan")) {
11884
+ if (norm2.includes("roadmap") || norm2.includes("lo trinh") || norm2.includes("milestone") || norm2.includes("giai doan")) {
11809
11885
  return void 0;
11810
11886
  }
11811
- if (norm.includes("symbol") || norm.includes("identifier ledger") || norm.includes("dinh danh")) {
11887
+ if (norm2.includes("symbol") || norm2.includes("identifier ledger") || norm2.includes("dinh danh")) {
11812
11888
  return "Symbol & Identifier Ledger";
11813
11889
  }
11814
- if (norm.includes("artifact contract") || norm.includes("hop dong hoc lieu")) {
11890
+ if (norm2.includes("artifact contract") || norm2.includes("hop dong hoc lieu")) {
11815
11891
  return "Artifact Contract";
11816
11892
  }
11817
- if (norm.includes("lesson design plan") || norm.includes("ke hoach thiet ke")) {
11893
+ if (norm2.includes("lesson design plan") || norm2.includes("ke hoach thiet ke")) {
11818
11894
  return "A. Lesson Design Plan";
11819
11895
  }
11820
- if (norm.includes("lesson flow") || norm.includes("tien trinh giang day")) {
11896
+ if (norm2.includes("lesson flow") || norm2.includes("tien trinh giang day")) {
11821
11897
  return "B. Lesson Flow";
11822
11898
  }
11823
- if (norm.includes("learning objective") || norm.includes("muc tieu bai hoc")) {
11899
+ if (norm2.includes("learning objective") || norm2.includes("muc tieu bai hoc")) {
11824
11900
  return "Learning Objectives & Evidence";
11825
11901
  }
11826
- if (norm.includes("activity sequence") || norm.includes("chuoi hoat dong")) {
11902
+ if (norm2.includes("activity sequence") || norm2.includes("chuoi hoat dong")) {
11827
11903
  return "Activity Sequence";
11828
11904
  }
11829
- if (norm.includes("key term") || norm.includes("thuat ngu")) {
11905
+ if (norm2.includes("key term") || norm2.includes("thuat ngu")) {
11830
11906
  return "Key Terms";
11831
11907
  }
11832
- if (norm.includes("concept narrative") || norm.includes("dien giai khai niem")) {
11908
+ if (norm2.includes("concept narrative") || norm2.includes("dien giai khai niem")) {
11833
11909
  return "Concept Narratives";
11834
11910
  }
11835
- if (norm.includes("worked example") || norm.includes("vi du")) {
11911
+ if (norm2.includes("worked example") || norm2.includes("vi du")) {
11836
11912
  return "Worked Micro-Examples";
11837
11913
  }
11914
+ if (norm2.includes("common mistake") || norm2.includes("loi thuong gap") || norm2.includes("sai lam") || norm2.includes("pitfall")) {
11915
+ return "Common Mistakes";
11916
+ }
11917
+ if (norm2.includes("self check") || norm2.includes("self-check") || norm2.includes("tu kiem tra")) {
11918
+ return "Self-Check Questions";
11919
+ }
11838
11920
  return void 0;
11839
11921
  }
11840
11922
  function truncateByBudget(source, budget) {
@@ -12769,7 +12851,7 @@ function decideScaffolds(session, nodeById, warnings) {
12769
12851
  return decisions;
12770
12852
  }
12771
12853
  var DEPTH_NEXT = { ulo: "cio", cio: "sio", sio: "sio" };
12772
- function assignDepths(nodeIds, nodeById) {
12854
+ function assignDepths(nodeIds, nodeById, conceptEncounterMap) {
12773
12855
  const assignments = [];
12774
12856
  let lastDepth = null;
12775
12857
  for (const id of nodeIds) {
@@ -12777,8 +12859,14 @@ function assignDepths(nodeIds, nodeById) {
12777
12859
  let depth;
12778
12860
  let source;
12779
12861
  if (node.kind === "concept") {
12862
+ const primaryCode = node.concept_codes[0];
12863
+ const encounters = primaryCode && conceptEncounterMap ? conceptEncounterMap.get(primaryCode) ?? 1 : 1;
12780
12864
  const advanced = node.phase_id !== "" && /__ADV\d{2}$/.test(node.id);
12781
- depth = advanced ? "sio" : "cio";
12865
+ if (encounters >= 2 || advanced) {
12866
+ depth = "sio";
12867
+ } else {
12868
+ depth = "cio";
12869
+ }
12782
12870
  source = "planner";
12783
12871
  } else {
12784
12872
  depth = node.depth_hint ?? "cio";
@@ -12793,6 +12881,201 @@ function assignDepths(nodeIds, nodeById) {
12793
12881
  }
12794
12882
  return assignments;
12795
12883
  }
12884
+ function computeConceptSpiralProgression(sessions, nodeById) {
12885
+ const encounters = [];
12886
+ const conceptCounts = /* @__PURE__ */ new Map();
12887
+ for (const s of sessions) {
12888
+ const sessionConcepts = [...new Set(s.node_ids.flatMap((id) => nodeById.get(id)?.concept_codes || []))];
12889
+ for (const cCode of sessionConcepts) {
12890
+ const count = (conceptCounts.get(cCode) || 0) + 1;
12891
+ conceptCounts.set(cCode, count);
12892
+ let bloomCap;
12893
+ let depth;
12894
+ if (count === 1) {
12895
+ bloomCap = "Understand";
12896
+ depth = "cio";
12897
+ } else if (count === 2) {
12898
+ bloomCap = "Apply";
12899
+ depth = "sio";
12900
+ } else {
12901
+ bloomCap = "Analyze";
12902
+ depth = "sio";
12903
+ }
12904
+ encounters.push({
12905
+ concept_code: cCode,
12906
+ session_id: s.id,
12907
+ encounter_index: count,
12908
+ bloom_cap: bloomCap,
12909
+ depth
12910
+ });
12911
+ }
12912
+ }
12913
+ return encounters;
12914
+ }
12915
+ function checkSessionZpd(sessionIndex, nodeIds, nodeById, allSeenConcepts, allSeenKeywords, entryKw, warnings) {
12916
+ const concepts = [...new Set(nodeIds.flatMap((id) => nodeById.get(id)?.concept_codes || []))];
12917
+ const newConcepts = concepts.filter((c) => !allSeenConcepts.has(c));
12918
+ const knownConcepts = concepts.filter((c) => allSeenConcepts.has(c));
12919
+ const allKeywords = [...new Set(nodeIds.flatMap((id) => nodeById.get(id)?.keywords || []))];
12920
+ const newKeywords = allKeywords.filter((k) => !allSeenKeywords.has(k.toLowerCase()) && !entryKw.has(k.toLowerCase()));
12921
+ const knownKeywords = allKeywords.filter((k) => allSeenKeywords.has(k.toLowerCase()) || entryKw.has(k.toLowerCase()));
12922
+ const issues = [];
12923
+ let verdict = "OK";
12924
+ if (concepts.length > 0 && newConcepts.length > 2) {
12925
+ verdict = "TOO_MANY_NEW";
12926
+ issues.push(`${newConcepts.length} new concepts exceed ZPD limit (max 2)`);
12927
+ } else if (concepts.length === 0 && newKeywords.length > 4) {
12928
+ verdict = "TOO_MANY_NEW";
12929
+ issues.push(`${newKeywords.length} new keywords exceed ZPD limit (max 4)`);
12930
+ }
12931
+ if (sessionIndex > 0) {
12932
+ if (concepts.length > 1 && knownConcepts.length === 0) {
12933
+ verdict = "NO_ZPD_BRIDGE";
12934
+ issues.push("No known concepts from previous sessions to bridge new learning");
12935
+ } else if (concepts.length === 0 && allKeywords.length > 2 && knownKeywords.length === 0) {
12936
+ verdict = "NO_ZPD_BRIDGE";
12937
+ issues.push("No known keywords from previous sessions to bridge new learning");
12938
+ }
12939
+ }
12940
+ let bridgeRepair;
12941
+ if (verdict === "NO_ZPD_BRIDGE" && (allSeenConcepts.size > 0 || allSeenKeywords.size > 0)) {
12942
+ const bridgeAnchor = [...allSeenConcepts].pop() || [...allSeenKeywords].pop() || "prior knowledge";
12943
+ bridgeRepair = {
12944
+ node_id: nodeIds[0],
12945
+ decision: "recap_in_lesson",
12946
+ reason: `ZPD self-healing: recap anchor (${bridgeAnchor}) to bridge into new material`
12947
+ };
12948
+ warnings.push(`ZPD bridge self-healing: session ${sessionIndex + 1} injected recap anchor (${bridgeAnchor})`);
12949
+ }
12950
+ for (const c of concepts) allSeenConcepts.add(c);
12951
+ for (const k of allKeywords) allSeenKeywords.add(k.toLowerCase());
12952
+ return {
12953
+ status: {
12954
+ verdict,
12955
+ new_concept_count: concepts.length > 0 ? newConcepts.length : newKeywords.length,
12956
+ known_concept_count: concepts.length > 0 ? knownConcepts.length : knownKeywords.length,
12957
+ issues
12958
+ },
12959
+ bridgeRepair
12960
+ };
12961
+ }
12962
+ function buildWalkingSkeleton(units, sessions) {
12963
+ const epitomeUnit = units[0];
12964
+ const epitomeId = epitomeUnit?.id || "U01";
12965
+ const epitomeDeliverables = sessions.filter((s) => s.unit_id === epitomeId).flatMap((s) => s.exit_evidence || []).slice(0, 8);
12966
+ const elaborations = units.slice(1).map((u) => ({
12967
+ unit_id: u.id,
12968
+ focus_area: u.name,
12969
+ elaborates_on: epitomeDeliverables.slice(0, 3)
12970
+ }));
12971
+ return {
12972
+ epitome_unit_id: epitomeId,
12973
+ epitome_deliverables: epitomeDeliverables,
12974
+ elaborations
12975
+ };
12976
+ }
12977
+ function buildMasteryGates(units, sessions) {
12978
+ const gates = [];
12979
+ for (let i = 0; i < units.length - 1; i++) {
12980
+ const cur = units[i];
12981
+ const next = units[i + 1];
12982
+ const unitEvidence = sessions.filter((s) => s.unit_id === cur.id).flatMap((s) => s.exit_evidence || []).slice(0, 6);
12983
+ gates.push({
12984
+ phase_or_unit_id: cur.id,
12985
+ gate_name: `${cur.name} Mastery Gate`,
12986
+ exit_criteria: unitEvidence.length > 0 ? unitEvidence : [`Master all deliverables for ${cur.name}`],
12987
+ next_unit_id: next.id,
12988
+ remediation: {
12989
+ trigger: "FAIL_GATE_CRITERIA",
12990
+ focus_minutes: 15,
12991
+ action: `Review and complete failing practical criteria for ${cur.name} before starting ${next.name}`
12992
+ }
12993
+ });
12994
+ }
12995
+ return gates;
12996
+ }
12997
+ function dropCyclicConceptEdges(edges) {
12998
+ const pairSet = new Set(edges.map((e) => `${e.concept_code}|${e.requires}`));
12999
+ const nonSymmetric = edges.filter((e) => !pairSet.has(`${e.requires}|${e.concept_code}`));
13000
+ const deps = /* @__PURE__ */ new Map();
13001
+ const createsCycle = (a, b) => {
13002
+ const stack = [b];
13003
+ const seen = /* @__PURE__ */ new Set();
13004
+ while (stack.length > 0) {
13005
+ const n = stack.pop();
13006
+ if (n === a) return true;
13007
+ if (seen.has(n)) continue;
13008
+ seen.add(n);
13009
+ const targets = deps.get(n);
13010
+ if (targets) {
13011
+ for (const t of targets) stack.push(t);
13012
+ }
13013
+ }
13014
+ return false;
13015
+ };
13016
+ const out = [];
13017
+ for (const e of nonSymmetric) {
13018
+ if (createsCycle(e.concept_code, e.requires)) continue;
13019
+ if (!deps.has(e.concept_code)) deps.set(e.concept_code, /* @__PURE__ */ new Set());
13020
+ deps.get(e.concept_code).add(e.requires);
13021
+ out.push(e);
13022
+ }
13023
+ return out;
13024
+ }
13025
+ function buildConceptPrerequisites(sessions, nodeById, edges) {
13026
+ const firstSeen = /* @__PURE__ */ new Map();
13027
+ for (let i = 0; i < sessions.length; i++) {
13028
+ const s = sessions[i];
13029
+ for (const nid of s.node_ids) {
13030
+ const codes = nodeById.get(nid)?.concept_codes || [];
13031
+ for (const c of codes) {
13032
+ if (!firstSeen.has(c)) firstSeen.set(c, i);
13033
+ }
13034
+ }
13035
+ }
13036
+ const rawEdges = [];
13037
+ const seenPair = /* @__PURE__ */ new Set();
13038
+ for (const e of edges) {
13039
+ const fromNode = nodeById.get(e.from);
13040
+ const toNode = nodeById.get(e.to);
13041
+ if (!fromNode || !toNode) continue;
13042
+ const fromCodes = fromNode.concept_codes || [];
13043
+ const toCodes = toNode.concept_codes || [];
13044
+ for (const tc of toCodes) {
13045
+ for (const fc of fromCodes) {
13046
+ if (tc === fc) continue;
13047
+ const key = `${tc}|${fc}`;
13048
+ if (seenPair.has(key)) continue;
13049
+ seenPair.add(key);
13050
+ const firstSeenTc = firstSeen.get(tc) ?? 999;
13051
+ const firstSeenFc = firstSeen.get(fc) ?? 999;
13052
+ const supported = firstSeenFc <= firstSeenTc;
13053
+ rawEdges.push({
13054
+ concept_code: tc,
13055
+ requires: fc,
13056
+ source: "CONCEPT_LEVEL_PREREQ",
13057
+ confidence: supported ? 0.8 : 0.6,
13058
+ rationale: e.reason || `Derived from planning node dependency: ${fromNode.name} -> ${toNode.name}`,
13059
+ structure_supported: supported,
13060
+ needs_review: !supported,
13061
+ hub: false
13062
+ });
13063
+ }
13064
+ }
13065
+ }
13066
+ const fanOut = /* @__PURE__ */ new Map();
13067
+ for (const re of rawEdges) {
13068
+ fanOut.set(re.concept_code, (fanOut.get(re.concept_code) || 0) + 1);
13069
+ }
13070
+ for (const re of rawEdges) {
13071
+ if ((fanOut.get(re.concept_code) || 0) >= 6) {
13072
+ re.hub = true;
13073
+ re.confidence = Math.min(re.confidence, 0.5);
13074
+ re.needs_review = true;
13075
+ }
13076
+ }
13077
+ return dropCyclicConceptEdges(rawEdges);
13078
+ }
12796
13079
  async function buildCurriculumPlan(rawGraph, options) {
12797
13080
  const { constraints } = options;
12798
13081
  const warnings = [];
@@ -12888,6 +13171,9 @@ async function buildCurriculumPlan(rawGraph, options) {
12888
13171
  const seenNodes = /* @__PURE__ */ new Set();
12889
13172
  const multiPartNodes = new Set(packed.flatMap((s) => s.entries.filter((e) => e.totalParts > 1).map((e) => e.id)));
12890
13173
  const sessions = [];
13174
+ const allSeenConcepts = /* @__PURE__ */ new Set();
13175
+ const allSeenKeywords = /* @__PURE__ */ new Set();
13176
+ const conceptEncounterMap = /* @__PURE__ */ new Map();
12891
13177
  for (let i = 0; i < packed.length; i++) {
12892
13178
  const s = packed[i];
12893
13179
  for (const id of s.nodeIds) {
@@ -12932,6 +13218,16 @@ async function buildCurriculumPlan(rawGraph, options) {
12932
13218
  knowledge: s.knowledgeMinutes || 0,
12933
13219
  practice: s.practiceMinutes + (s.practiceMinutes === 0 && s.knowledgeMinutes > 0 ? Math.min(spareMinutes, 10) : 0)
12934
13220
  };
13221
+ const sessionConcepts = [...new Set(s.nodeIds.flatMap((id) => nodeById.get(id)?.concept_codes || []))];
13222
+ for (const cCode of sessionConcepts) {
13223
+ const curCount = (conceptEncounterMap.get(cCode) || 0) + 1;
13224
+ conceptEncounterMap.set(cCode, curCount);
13225
+ }
13226
+ const zpdResult = checkSessionZpd(i, s.nodeIds, nodeById, allSeenConcepts, allSeenKeywords, entryKw, warnings);
13227
+ const prereqDecisions = decidePrerequisites(ctx, nodeById, graph.edges, sessionIndexByNode, sessionOrderByIndex, constraints);
13228
+ if (zpdResult.bridgeRepair) {
13229
+ prereqDecisions.unshift(zpdResult.bridgeRepair);
13230
+ }
12935
13231
  sessions.push({
12936
13232
  id: lessonCode,
12937
13233
  unit_id: unit.id,
@@ -12952,8 +13248,8 @@ async function buildCurriculumPlan(rawGraph, options) {
12952
13248
  // Spiral pedagogy = apply at greater depth, not re-lecture.
12953
13249
  practice_minutes: sessionMinutes.practice,
12954
13250
  overhead_minutes: spec.overheadMinutes,
12955
- depth_assignments: assignDepths(s.nodeIds, nodeById),
12956
- prerequisite_decisions: decidePrerequisites(ctx, nodeById, graph.edges, sessionIndexByNode, sessionOrderByIndex, constraints),
13251
+ depth_assignments: assignDepths(s.nodeIds, nodeById, conceptEncounterMap),
13252
+ prerequisite_decisions: prereqDecisions,
12957
13253
  scaffold_decisions: decideScaffolds(ctx, nodeById, warnings),
12958
13254
  // Deliverable phrasing follows the node role: concept → evidence is the
12959
13255
  // explanation, product_step/skill → the artifact or activity itself.
@@ -12968,16 +13264,58 @@ async function buildCurriculumPlan(rawGraph, options) {
12968
13264
  bronze: "Complete the core deliverable for " + nodeNames[0] + " with provided scaffolding.",
12969
13265
  silver: "Complete all session deliverables independently.",
12970
13266
  gold: "Extend the deliverable: combine " + nodeNames.join(", ") + " in a self-chosen variant."
12971
- }
13267
+ },
13268
+ // P50: ZPD cognitive load status for this session
13269
+ zpd_status: zpdResult.status
12972
13270
  });
12973
13271
  }
12974
13272
  const unassigned = graph.nodes.map((n) => n.id).filter((id) => !seenNodes.has(id));
12975
13273
  if (unassigned.length > 0) throw new Error("Coverage violation: unassigned nodes " + unassigned.join(", "));
13274
+ if (options.llmFn && sessions.length > 0) {
13275
+ try {
13276
+ const sessionSummaries = sessions.map((s) => `${s.id}|${s.title}|objective: ${s.prose_objective.slice(0, 120)}|keywords: ${s.new_keywords.join("/")}`).join("\n");
13277
+ const dSys = 'You differentiate learning tasks. Output ONLY a JSON object mapping session-id to {"bronze":string,"silver":string,"gold":string}. Each tier ONE sentence: bronze = foundational task with provided scaffolding; silver = independent application; gold = creative extension combining session concepts. Same language as the objectives.';
13278
+ const dUsr = `Age band: ${constraints.age_band[0]}-${constraints.age_band[1]}. Entry level: ${constraints.entry_level}.
13279
+ Sessions:
13280
+ ${sessionSummaries}`;
13281
+ const raw = (await options.llmFn(dSys, dUsr)).trim();
13282
+ const match = raw.match(/\{[\s\S]*\}/);
13283
+ if (match) {
13284
+ const parsed = JSON.parse(match[0]);
13285
+ let upgraded = 0;
13286
+ for (const s of sessions) {
13287
+ const d = parsed[s.id];
13288
+ if (d && typeof d.bronze === "string" && d.bronze.length >= 10 && typeof d.silver === "string" && d.silver.length >= 10 && typeof d.gold === "string" && d.gold.length >= 10) {
13289
+ s.differentiation = { bronze: d.bronze, silver: d.silver, gold: d.gold };
13290
+ upgraded++;
13291
+ }
13292
+ }
13293
+ if (upgraded < sessions.length) warnings.push(`differentiation_llm_partial:${upgraded}/${sessions.length}`);
13294
+ } else {
13295
+ warnings.push("differentiation_llm_unparseable");
13296
+ }
13297
+ } catch (e) {
13298
+ warnings.push("differentiation_llm_failed:" + (e instanceof Error ? e.message.slice(0, 80) : String(e).slice(0, 80)));
13299
+ }
13300
+ }
12976
13301
  const glossaryScope = sessions.map((s) => ({
12977
13302
  session_id: s.id,
12978
13303
  terms: [...new Set(s.node_ids.flatMap((id) => nodeById.get(id).keywords))]
12979
13304
  }));
12980
- const planPayload = { units, sessions, course: { objectives: courseObjectives }, constraints };
13305
+ const conceptSpiralProgression = computeConceptSpiralProgression(sessions, nodeById);
13306
+ const walkingSkeleton = buildWalkingSkeleton(units, sessions);
13307
+ const masteryGates = buildMasteryGates(units, sessions);
13308
+ const conceptPrerequisites = buildConceptPrerequisites(sessions, nodeById, graph.edges);
13309
+ const planPayload = {
13310
+ units,
13311
+ sessions,
13312
+ course: { objectives: courseObjectives },
13313
+ constraints,
13314
+ walking_skeleton: walkingSkeleton,
13315
+ mastery_gates: masteryGates,
13316
+ concept_spiral_progression: conceptSpiralProgression,
13317
+ concept_prerequisites: conceptPrerequisites
13318
+ };
12981
13319
  const planHash = crypto.createHash("sha256").update(JSON.stringify(planPayload)).digest("hex").slice(0, 16);
12982
13320
  const plan = CurriculumPlanSchema.parse({
12983
13321
  schema_version: 1,
@@ -12989,6 +13327,10 @@ async function buildCurriculumPlan(rawGraph, options) {
12989
13327
  course: { objectives: courseObjectives, capstone_ref: constraints.capstone },
12990
13328
  units,
12991
13329
  sessions,
13330
+ walking_skeleton: walkingSkeleton,
13331
+ mastery_gates: masteryGates,
13332
+ concept_spiral_progression: conceptSpiralProgression,
13333
+ concept_prerequisites: conceptPrerequisites,
12992
13334
  glossary_scope: glossaryScope,
12993
13335
  translation: options.translation ?? { policy: "at_publish", target_language: null },
12994
13336
  coverage_report: { assigned_node_ids: [...seenNodes], unassigned_node_ids: [], warnings },
@@ -13020,6 +13362,13 @@ function buildSessionSliceContext(plan, lessonCode) {
13020
13362
  lines.push("- Prerequisite keywords: " + (s.prerequisite_keywords.join(", ") || "(none)"));
13021
13363
  lines.push("- Time split: knowledge " + s.knowledge_minutes + "m / practice " + s.practice_minutes + "m / overhead " + s.overhead_minutes + "m");
13022
13364
  lines.push("- Depth assignments: " + s.depth_assignments.map((d) => d.node_id + "=" + d.depth.toUpperCase() + "(" + d.source + ")").join("; "));
13365
+ if (s.zpd_status && s.zpd_status.verdict !== "OK") {
13366
+ lines.push("- ZPD alert: " + s.zpd_status.verdict + " (" + s.zpd_status.issues.join("; ") + ")");
13367
+ }
13368
+ const sessionSpirals = (plan.concept_spiral_progression || []).filter((e) => e.session_id === lessonCode);
13369
+ if (sessionSpirals.length > 0) {
13370
+ lines.push("- Spiral encounters: " + sessionSpirals.map((e) => `${e.concept_code}#${e.encounter_index}=${e.bloom_cap}(${e.depth.toUpperCase()})`).join("; "));
13371
+ }
13023
13372
  if (s.prerequisite_decisions.length > 0) {
13024
13373
  lines.push("- Prerequisite decisions [" + s.prerequisite_decisions.length + "]:");
13025
13374
  for (const d of s.prerequisite_decisions) lines.push(" * " + d.node_id + ": " + d.decision + " \u2014 " + d.reason);
@@ -13341,14 +13690,14 @@ function lintFrameworkPack(pack) {
13341
13690
  const primaryLang = pack.manifest.languages[0];
13342
13691
  const text = s.texts[primaryLang];
13343
13692
  if (!text) continue;
13344
- const norm = text.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/).filter((w) => w.length > 2).sort().join(" ");
13345
- if (!norm) continue;
13346
- const prev = seen.get(norm);
13693
+ const norm2 = text.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/).filter((w) => w.length > 2).sort().join(" ");
13694
+ if (!norm2) continue;
13695
+ const prev = seen.get(norm2);
13347
13696
  if (prev !== void 0) {
13348
13697
  issues.push({ severity: "WARNING", code: "DUP_TEXT_EXACT", message: `Text is token-identical to statement "${prev}"`, ref: s.id });
13349
13698
  } else {
13350
13699
  for (const [otherNorm, otherId] of seen.entries()) {
13351
- const a = new Set(norm.split(" "));
13700
+ const a = new Set(norm2.split(" "));
13352
13701
  const b = new Set(otherNorm.split(" "));
13353
13702
  let inter = 0;
13354
13703
  for (const w of a) if (b.has(w)) inter++;
@@ -13358,7 +13707,7 @@ function lintFrameworkPack(pack) {
13358
13707
  break;
13359
13708
  }
13360
13709
  }
13361
- seen.set(norm, s.id);
13710
+ seen.set(norm2, s.id);
13362
13711
  }
13363
13712
  }
13364
13713
  for (const m of pack.mappings ?? []) {
@@ -26099,6 +26448,263 @@ function extractStandardRefs(text) {
26099
26448
  return Array.from(new Set(matches));
26100
26449
  }
26101
26450
 
26451
+ // src/services/contextSlots.ts
26452
+ var LESSON_SLOT_BUDGETS = {
26453
+ ACT: { budget: 8e3 },
26454
+ GUIDE: { budget: 6e3 },
26455
+ QUIZ: { budget: 3500, priorities: ["A. Lesson Design Plan"] },
26456
+ SLIDE: { budget: 4500, priorities: ["B. Lesson Flow", "A. Lesson Design Plan"] },
26457
+ WKS: { budget: 4500, priorities: ["A. Lesson Design Plan", "B. Lesson Flow"] },
26458
+ EXIT_TICKET: { budget: 3e3, priorities: ["A. Lesson Design Plan", "B. Lesson Flow"] }
26459
+ };
26460
+ var KX_ONLY_TYPES = /* @__PURE__ */ new Set(["CODE", "HANDOUT", "EXT"]);
26461
+ var KX_SLOT_PRIORITIES = {
26462
+ QUIZ: ["Common Mistakes", "Self-Check Questions", "Key Terms", "Concept Narratives"],
26463
+ EXIT_TICKET: ["Self-Check Questions", "Key Terms"],
26464
+ WKS: ["Self-Check Questions", "Worked Micro-Examples", "Key Terms"],
26465
+ CODE: ["Worked Micro-Examples", "Key Terms", "Common Mistakes"],
26466
+ GUIDE: ["Common Mistakes", "Key Terms", "Concept Narratives"],
26467
+ HANDOUT: ["Key Terms", "Concept Narratives", "Common Mistakes"],
26468
+ SLIDE: ["Key Terms", "Worked Micro-Examples"],
26469
+ ACT: ["Worked Micro-Examples", "Common Mistakes"],
26470
+ EXT: ["Worked Micro-Examples", "Common Mistakes"]
26471
+ };
26472
+ var norm = (s) => s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "");
26473
+ function extractActivityContractRows(lessonContent, artifactType, maxChars = 1500) {
26474
+ if (!lessonContent) return "";
26475
+ const wanted = norm(artifactType).replace(/_/g, "");
26476
+ const lines = lessonContent.split("\n");
26477
+ let inSeq = false;
26478
+ const headerRows = [];
26479
+ const matchedRows = [];
26480
+ const allRows = [];
26481
+ for (let i = 0; i < lines.length; i++) {
26482
+ const line = lines[i];
26483
+ const h = line.match(/^#{2,4}\s+(.*)$/);
26484
+ if (h) {
26485
+ const t2 = h[1];
26486
+ if (/activity\s*sequence/i.test(t2)) inSeq = true;
26487
+ else if (inSeq) break;
26488
+ continue;
26489
+ }
26490
+ if (!inSeq) continue;
26491
+ const t = line.trim();
26492
+ if (!t.startsWith("|")) continue;
26493
+ if (/^\|\s*[-: |]+\|\s*$/.test(t)) continue;
26494
+ const cells = t.split("|").slice(1, -1).map((c) => c.trim());
26495
+ if (cells.length >= 4) {
26496
+ if (headerRows.length === 0) {
26497
+ headerRows.push(t);
26498
+ continue;
26499
+ }
26500
+ allRows.push(t);
26501
+ const contract = cells[cells.length - 1] ?? "";
26502
+ const candidates = contract.split(/[,;/]/).map((c) => norm(c));
26503
+ if (candidates.some((c) => c === wanted || c.length > 3 && (c.includes(wanted) || wanted.includes(c)))) {
26504
+ matchedRows.push(t);
26505
+ }
26506
+ }
26507
+ }
26508
+ const rows = matchedRows.length > 0 ? matchedRows : allRows.slice(0, 3);
26509
+ if (rows.length === 0) return "";
26510
+ let out = headerRows[0] ?? "";
26511
+ for (const r of rows) {
26512
+ if (out.length + r.length + 1 > maxChars) break;
26513
+ out += "\n" + r;
26514
+ }
26515
+ return out;
26516
+ }
26517
+ function extractTieredScaffoldingBlock(lessonContent, maxChars = 900) {
26518
+ if (!lessonContent) return "";
26519
+ const lines = lessonContent.split("\n");
26520
+ let capture = null;
26521
+ for (const line of lines) {
26522
+ const h = line.match(/^#{2,4}\s+(.*)$/);
26523
+ if (h) {
26524
+ const isTierHeading = /elaborate|tiered|scaffold/i.test(h[1] ?? "");
26525
+ if (capture && !isTierHeading) break;
26526
+ if (isTierHeading) capture = [];
26527
+ continue;
26528
+ }
26529
+ if (capture) {
26530
+ capture.push(line);
26531
+ if (capture.join("").length >= maxChars) break;
26532
+ }
26533
+ }
26534
+ const body = (capture ?? []).join("\n").trim();
26535
+ return body ? body.slice(0, maxChars) : "";
26536
+ }
26537
+ function extractAssessmentMap(lessonContent, maxChars = 1200) {
26538
+ if (!lessonContent) return "";
26539
+ const lines = lessonContent.split("\n");
26540
+ let capture = null;
26541
+ for (const line of lines) {
26542
+ const h = line.match(/^#{2,4}\s+(.*)$/);
26543
+ if (h) {
26544
+ const isTarget = /assessment\s*map/i.test(h[1] ?? "");
26545
+ if (capture && !isTarget) break;
26546
+ if (isTarget) capture = [];
26547
+ continue;
26548
+ }
26549
+ if (capture) {
26550
+ capture.push(line);
26551
+ if (capture.join("").length >= maxChars) break;
26552
+ }
26553
+ }
26554
+ const body = (capture ?? []).join("\n").trim();
26555
+ return body ? body.slice(0, maxChars) : "";
26556
+ }
26557
+ function blockMeta(slot, source, text, verified, issues = []) {
26558
+ return { slot, source, chars: text.length, verified, issues };
26559
+ }
26560
+ function buildSatelliteContext(input) {
26561
+ const {
26562
+ artifactType,
26563
+ commonContext,
26564
+ lessonContent,
26565
+ lessonExcerpt,
26566
+ expositionContent,
26567
+ slcMarkdown,
26568
+ symbolLedgerBlock,
26569
+ pedagogyLabel,
26570
+ mode = "legacy"
26571
+ } = input;
26572
+ const type = (artifactType || "").toUpperCase().trim();
26573
+ if (mode === "legacy" || type === "LESSON") {
26574
+ const context2 = `${commonContext}
26575
+
26576
+ [CANONICAL LESSON PLAN (${pedagogyLabel ?? "LESSON"})]:
26577
+ ${lessonExcerpt.excerpt}${symbolLedgerBlock}`;
26578
+ return {
26579
+ context: context2,
26580
+ blocks: [
26581
+ blockMeta("commonContext", "COMPOSITE", commonContext, true),
26582
+ blockMeta("lessonExcerpt", "LESSON", lessonExcerpt.excerpt, lessonExcerpt.verified, lessonExcerpt.issues)
26583
+ ],
26584
+ verified: lessonExcerpt.verified,
26585
+ sectionAware: lessonExcerpt.sectionAware,
26586
+ issues: lessonExcerpt.issues,
26587
+ tokenEstimate: Math.round(context2.length / 4),
26588
+ canonicalExcerpt: lessonExcerpt.excerpt
26589
+ };
26590
+ }
26591
+ const blocks = [];
26592
+ const issues = [];
26593
+ const parts = [commonContext];
26594
+ blocks.push(blockMeta("commonContext", "COMPOSITE", commonContext, true));
26595
+ let excerptSlot;
26596
+ let miniSlot;
26597
+ const compositeHasKx = /###\s+KNOWLEDGE_EXPOSITION/.test(commonContext);
26598
+ if (expositionContent.trim() && !compositeHasKx) {
26599
+ const kxExcerpt = buildSectionAwareExcerpt(expositionContent, {
26600
+ priorities: KX_SLOT_PRIORITIES[type] ?? ["Key Terms", "Concept Narratives", "Worked Micro-Examples"],
26601
+ budget: 4500,
26602
+ sectionLanguageContract: slcMarkdown,
26603
+ artifactType: "KNOWLEDGE_EXPOSITION"
26604
+ });
26605
+ parts.push(`
26606
+
26607
+ [KNOWLEDGE_EXPOSITION (canonical knowledge \u2014 teach from this, do not contradict; scoped for ${type})]:
26608
+ ${kxExcerpt.excerpt}`);
26609
+ blocks.push(blockMeta("kxExcerpt:" + type, "KNOWLEDGE_EXPOSITION", kxExcerpt.excerpt, kxExcerpt.verified, kxExcerpt.issues));
26610
+ if (!kxExcerpt.verified) issues.push(...kxExcerpt.issues.map((i) => `kx:${i}`));
26611
+ }
26612
+ if (!KX_ONLY_TYPES.has(type)) {
26613
+ const spec = LESSON_SLOT_BUDGETS[type] ?? { budget: 12e3 };
26614
+ const excerpt = buildSectionAwareExcerpt(lessonContent, {
26615
+ priorities: spec.priorities ?? ["Artifact Contract", "A. Lesson Design Plan", "B. Lesson Flow", "Learning Objectives & Evidence", "Activity Sequence"],
26616
+ budget: spec.budget,
26617
+ sectionLanguageContract: slcMarkdown,
26618
+ artifactType: "LESSON"
26619
+ });
26620
+ const excerptBlock = `
26621
+
26622
+ [CANONICAL LESSON PLAN (scoped for ${type})]:
26623
+ ${excerpt.excerpt}`;
26624
+ parts.push(excerptBlock);
26625
+ excerptSlot = excerptBlock;
26626
+ blocks.push(blockMeta("lessonExcerpt:" + type, "LESSON", excerpt.excerpt, excerpt.verified, excerpt.issues));
26627
+ if (!excerpt.verified) issues.push(...excerpt.issues.map((i) => `lesson:${i}`));
26628
+ if (type === "QUIZ" || type === "WKS") {
26629
+ const am = extractAssessmentMap(lessonContent, 1200);
26630
+ if (am) {
26631
+ parts.push(`
26632
+
26633
+ [ASSESSMENT MAP (criteria anchor \u2014 assess exactly these)]:
26634
+ ${am}`);
26635
+ blocks.push(blockMeta("assessmentMap:" + type, "LESSON", am, true));
26636
+ } else {
26637
+ issues.push(`assessment-map:unresolved:${type}`);
26638
+ }
26639
+ }
26640
+ if (symbolLedgerBlock) {
26641
+ parts.push(symbolLedgerBlock);
26642
+ blocks.push(blockMeta("symbolLedger", "LESSON", symbolLedgerBlock, true));
26643
+ }
26644
+ } else {
26645
+ const contractRows = extractActivityContractRows(lessonContent, type === "CODE" ? "CODE_LAB" : type, 1500);
26646
+ const tierBlock = type === "CODE" || type === "EXT" ? extractTieredScaffoldingBlock(lessonContent, 900) : "";
26647
+ const mini = [
26648
+ contractRows ? `### Activity Sequence rows assigned to ${type} (canonical \u2014 do not invent a different task):
26649
+ ${contractRows}` : "",
26650
+ tierBlock ? `### Tiered task ladder (from LESSON Elaborate phase):
26651
+ ${tierBlock}` : ""
26652
+ ].filter(Boolean).join("\n\n");
26653
+ if (mini) {
26654
+ const miniBlock = `
26655
+
26656
+ [LESSON ACTIVITY CONTRACT (mini-slot)]:
26657
+ ${mini}`;
26658
+ parts.push(miniBlock);
26659
+ miniSlot = miniBlock;
26660
+ blocks.push(blockMeta("activityContract:" + type, "LESSON", mini, true));
26661
+ } else {
26662
+ issues.push(`activity-contract:unresolved:${type}`);
26663
+ blocks.push(blockMeta("activityContract:" + type, "LESSON", "", false, ["no-matching-rows"]));
26664
+ }
26665
+ if (type === "CODE" && symbolLedgerBlock) {
26666
+ parts.push(symbolLedgerBlock);
26667
+ blocks.push(blockMeta("symbolLedger", "LESSON", symbolLedgerBlock, true));
26668
+ }
26669
+ }
26670
+ const context = parts.join("");
26671
+ return {
26672
+ context,
26673
+ blocks,
26674
+ verified: blocks.filter((b) => b.slot !== "commonContext").every((b) => b.verified),
26675
+ sectionAware: blocks.some((b) => b.slot.startsWith("lessonExcerpt") || b.slot.startsWith("kxExcerpt")),
26676
+ issues,
26677
+ tokenEstimate: Math.round(context.length / 4),
26678
+ // Judge ground truth = exactly what THIS type consumed (scoped or mini slot).
26679
+ canonicalExcerpt: (miniSlot ?? excerptSlot ?? lessonExcerpt.excerpt).trim()
26680
+ };
26681
+ }
26682
+ var ACTIVITY_ALIGNMENT_LOG_ONLY_UNTIL = /* @__PURE__ */ new Date("2026-09-23T00:00:00Z");
26683
+ function isActivityAlignmentLogOnly(now = /* @__PURE__ */ new Date()) {
26684
+ const env = (process.env.DRIFT_ALIGNMENT_LOG_ONLY ?? "").toLowerCase().trim();
26685
+ if (env === "false" || env === "0") return false;
26686
+ if (env === "true" || env === "1") return true;
26687
+ return now < ACTIVITY_ALIGNMENT_LOG_ONLY_UNTIL;
26688
+ }
26689
+ function evaluateActivityAlignment(canonicalExcerpt, generatedContent, now = /* @__PURE__ */ new Date()) {
26690
+ const mode = isActivityAlignmentLogOnly(now) ? "log_only" : "enforcing";
26691
+ if (!canonicalExcerpt || !canonicalExcerpt.trim()) {
26692
+ return { checked: false, passed: null, matchedTokens: [], missingTokens: [], mode };
26693
+ }
26694
+ const content = (generatedContent || "").toLowerCase();
26695
+ const stripped = canonicalExcerpt.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
26696
+ const latin = Array.from(new Set(stripped.match(/[a-z][a-z0-9_]{3,}/g) ?? []));
26697
+ const cjk = Array.from(new Set(canonicalExcerpt.match(/[\u4e00-\u9fff]{2,}/g) ?? []));
26698
+ const tokens = [...latin, ...cjk];
26699
+ if (tokens.length === 0) {
26700
+ return { checked: false, passed: null, matchedTokens: [], missingTokens: [], mode };
26701
+ }
26702
+ const matchedTokens = tokens.filter((t) => content.includes(t));
26703
+ const missingTokens = tokens.filter((t) => !content.includes(t));
26704
+ const passed = matchedTokens.length > 0;
26705
+ return { checked: true, passed, matchedTokens: matchedTokens.slice(0, 10), missingTokens: missingTokens.slice(0, 10), mode };
26706
+ }
26707
+
26102
26708
  // src/services/lessonProductionService.ts
26103
26709
  var __filename2 = typeof (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) === "string" && typeof url.fileURLToPath === "function" ? url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))) : "";
26104
26710
  var _resolvedDir = __filename2 ? path3__default.default.dirname(__filename2) : typeof __dirname !== "undefined" ? __dirname : process.cwd();
@@ -26671,9 +27277,9 @@ ${renderHorizonPromptBlock(horizon)}`;
26671
27277
  console.warn(`[produceSingleLesson] Notice: Curriculum Horizon extraction:`, hErr?.message || hErr);
26672
27278
  }
26673
27279
  let productSpecBlock = "";
26674
- const buildGroundTruthBlock = () => {
27280
+ const buildGroundTruthBlock = (includeKx = true) => {
26675
27281
  const parts = [];
26676
- if (expositionContext) {
27282
+ if (includeKx && expositionContext) {
26677
27283
  parts.push(`### KNOWLEDGE_EXPOSITION (CANONICAL KNOWLEDGE \u2014 teach from this, do not contradict)
26678
27284
  ${expositionContext}`);
26679
27285
  }
@@ -26707,10 +27313,10 @@ ${sessionSliceContext}` : "";
26707
27313
  const guardrailBlock = `
26708
27314
 
26709
27315
  ${platformGuardrail}`;
26710
- const assembleCommonContext = (sg) => `${baseContextPrefix}
27316
+ const assembleCommonContext = (sg, includeKx = true) => `${baseContextPrefix}
26711
27317
 
26712
27318
  [CONTENT STYLE GUIDE EXCERPT]:
26713
- ${sg}${standardsBlock}${glossaryBlock}${buildGroundTruthBlock()}${sessionSliceBlock}${horizonBlock}${guardrailBlock}`;
27319
+ ${sg}${standardsBlock}${glossaryBlock}${buildGroundTruthBlock(includeKx)}${sessionSliceBlock}${horizonBlock}${guardrailBlock}`;
26714
27320
  let commonContext = assembleCommonContext(effectiveStyleGuide);
26715
27321
  const MAX_COMPOSITE_PROMPT_CHARS = 3e4;
26716
27322
  const runnerOptions = options.customInference ? { customInference: options.customInference } : {};
@@ -27119,10 +27725,34 @@ ${currentContent}` }],
27119
27725
  - Entry Source File: \`${symbolLedger.entryFileName || symbolLedger.primarySymbol}\`
27120
27726
  - Key Identifiers to inherit verbatim: ${symbolLedger.keySymbols.map((s) => `\`${s}\``).join(", ")}
27121
27727
  - INVARIANT: You MUST use these exact identifier names in all code examples, exercises, and test assertions. Do NOT invent new struct/class names!` : "";
27122
- const satelliteContext = `${commonContext}
27123
-
27124
- [CANONICAL LESSON PLAN (${pedagogyLabel})]:
27125
- ${lessonExcerpt}${symbolLedgerBlock}`;
27728
+ const routingMode = options.contextRoutingMode ?? "legacy";
27729
+ const kxFreeCommonContext = routingMode === "hybrid" ? assembleCommonContext(effectiveStyleGuide, false) : "";
27730
+ const satelliteContexts = {};
27731
+ const satelliteContextMeta = {};
27732
+ const satelliteContextFor = (artifactType) => {
27733
+ const key = artifactType.toUpperCase();
27734
+ if (!satelliteContexts[key]) {
27735
+ const built = buildSatelliteContext({
27736
+ artifactType: key,
27737
+ commonContext: routingMode === "hybrid" ? kxFreeCommonContext : commonContext,
27738
+ lessonContent,
27739
+ lessonExcerpt: lessonExcerptResult,
27740
+ expositionContent: expositionContext,
27741
+ slcMarkdown,
27742
+ symbolLedgerBlock,
27743
+ pedagogyLabel,
27744
+ mode: routingMode
27745
+ });
27746
+ satelliteContexts[key] = built.context;
27747
+ satelliteContextMeta[key] = built;
27748
+ onProgress?.(
27749
+ "@content",
27750
+ `[CONTEXT] ${key} satellite context: ${built.context.length} chars (${built.blocks.map((b) => `${b.slot}:${b.chars}`).join(", ")})`,
27751
+ { type: "progress", promptChars: built.context.length, artifactType: key }
27752
+ );
27753
+ }
27754
+ return satelliteContexts[key];
27755
+ };
27126
27756
  const judgeSat = (sat, content) => {
27127
27757
  if (gateModeFor(gates, sat) !== "LLM_JUDGE") return Promise.resolve();
27128
27758
  const det = validateArtifactDeterministic({ content, refPack });
@@ -27142,6 +27772,30 @@ ${lessonExcerpt}${symbolLedgerBlock}`;
27142
27772
  }
27143
27773
  });
27144
27774
  }
27775
+ const slotMeta = satelliteContextMeta[sat.toUpperCase()];
27776
+ if (slotMeta) {
27777
+ const alignment = evaluateActivityAlignment(slotMeta.canonicalExcerpt, content);
27778
+ if (alignment.checked && alignment.passed === false) {
27779
+ const note = `activity-alignment drift vs ${sat} contract slot (matched: ${alignment.matchedTokens.join(", ") || "none"}); canonical tokens absent from output`;
27780
+ if (alignment.mode === "enforcing") {
27781
+ const critique = validatorRepairPrompt([{ code: "scope-drift", detail: note, evidence: alignment.missingTokens }]);
27782
+ onProgress?.("@reviewer", `\u26D4 ${sat} activity-alignment drift FAIL: ${note}`);
27783
+ return storage.updateArtifactState(projectId, lessonCode, sat, {
27784
+ state: "rejected",
27785
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
27786
+ contentHash: computeContentHash(content),
27787
+ review: {
27788
+ decision: "NEEDS_REVISION",
27789
+ reviewedBy: "@heuristic-linter",
27790
+ reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
27791
+ score: 0,
27792
+ critique
27793
+ }
27794
+ });
27795
+ }
27796
+ onProgress?.("@reviewer", `\u{1F9ED} [log-only] ${sat} activity-alignment drift: ${note}`);
27797
+ }
27798
+ }
27145
27799
  return judgeSatelliteArtifact({
27146
27800
  storage,
27147
27801
  projectId,
@@ -27154,7 +27808,7 @@ ${lessonExcerpt}${symbolLedgerBlock}`;
27154
27808
  onProgress,
27155
27809
  targetLang,
27156
27810
  canonicalLessonContent: lessonContent,
27157
- canonicalLessonExcerpt: lessonExcerpt
27811
+ canonicalLessonExcerpt: slotMeta?.canonicalExcerpt ?? lessonExcerpt
27158
27812
  });
27159
27813
  };
27160
27814
  const actRelPath = `_content/${unitCode}/ACT_${lessonCode}.md`;
@@ -27203,7 +27857,7 @@ ${languageDirective}
27203
27857
  ${buildHeadingDirective("ACT", slcMarkdown, targetLang)}`;
27204
27858
  const rawAct = await runCurriculumAIInference(
27205
27859
  [{ role: "user", content: actPrompt }],
27206
- satelliteContext,
27860
+ satelliteContextFor("ACT"),
27207
27861
  runnerOptions,
27208
27862
  (chunk, type) => {
27209
27863
  options.onProgress?.("@activity", chunk, { type: type || "content", artifactType: "ACT" });
@@ -27273,7 +27927,7 @@ ${languageDirective}
27273
27927
  ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
27274
27928
  const rawQuiz = await runCurriculumAIInference(
27275
27929
  [{ role: "user", content: quizPrompt }],
27276
- satelliteContext,
27930
+ satelliteContextFor("QUIZ"),
27277
27931
  runnerOptions,
27278
27932
  (chunk, type) => {
27279
27933
  options.onProgress?.("@assessor", chunk, { type: type || "content", artifactType: "QUIZ" });
@@ -27326,7 +27980,7 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
27326
27980
  headingDirective: buildHeadingDirective("SLIDE", slcMarkdown, targetLang),
27327
27981
  groundContext: slideGroundContext,
27328
27982
  productSpecBlock: productSpecBlock || void 0,
27329
- satelliteContext,
27983
+ satelliteContext: satelliteContextFor("SLIDE"),
27330
27984
  runnerOptions,
27331
27985
  onProgress: (agent, msg, meta) => {
27332
27986
  options.onProgress?.(agent, msg, meta || { type: "content", artifactType: "SLIDE" });
@@ -27410,7 +28064,7 @@ footer: "Curriculum OS \u2022 ${lessonCode} (${pedagogyLabel})"
27410
28064
  });
27411
28065
  const rawSlide = await runCurriculumAIInference(
27412
28066
  [{ role: "user", content: slidePrompt }],
27413
- satelliteContext,
28067
+ satelliteContextFor("SLIDE"),
27414
28068
  runnerOptions,
27415
28069
  (chunk, type) => {
27416
28070
  options.onProgress?.("@illustrator", chunk, { type: type || "content", artifactType: "SLIDE" });
@@ -27471,7 +28125,7 @@ ${languageDirective}
27471
28125
  ${buildHeadingDirective("GUIDE", slcMarkdown, targetLang)}`;
27472
28126
  const rawGuide = await runCurriculumAIInference(
27473
28127
  [{ role: "user", content: guidePrompt }],
27474
- satelliteContext,
28128
+ satelliteContextFor("GUIDE"),
27475
28129
  runnerOptions,
27476
28130
  (chunk, type) => {
27477
28131
  options.onProgress?.("@content", chunk, { type: type || "chunk", artifactType: "GUIDE" });
@@ -27523,7 +28177,7 @@ ${languageDirective}
27523
28177
  ${buildHeadingDirective("HANDOUT", slcMarkdown, targetLang)}`;
27524
28178
  const rawHandout = await runCurriculumAIInference(
27525
28179
  [{ role: "user", content: handoutPrompt }],
27526
- satelliteContext,
28180
+ satelliteContextFor("HANDOUT"),
27527
28181
  runnerOptions,
27528
28182
  (chunk, type) => {
27529
28183
  options.onProgress?.("@content", chunk, { type: type || "chunk", artifactType: "HANDOUT" });
@@ -27579,7 +28233,7 @@ ${languageDirective}
27579
28233
  ${buildHeadingDirective("WKS", slcMarkdown, targetLang)}`;
27580
28234
  const rawWks = await runCurriculumAIInference(
27581
28235
  [{ role: "user", content: wksPrompt }],
27582
- satelliteContext,
28236
+ satelliteContextFor("WKS"),
27583
28237
  runnerOptions,
27584
28238
  (chunk, type) => {
27585
28239
  options.onProgress?.("@content", chunk, { type: type || "chunk", artifactType: "WKS" });
@@ -27639,7 +28293,7 @@ ${languageDirective}
27639
28293
  ${buildHeadingDirective("CODE", slcMarkdown, targetLang)}`;
27640
28294
  const rawCode = await runCurriculumAIInference(
27641
28295
  [{ role: "user", content: codePrompt }],
27642
- satelliteContext,
28296
+ satelliteContextFor("CODE"),
27643
28297
  runnerOptions,
27644
28298
  (chunk, type) => {
27645
28299
  options.onProgress?.("@activity", chunk, { type: type || "chunk", artifactType: "CODE" });
@@ -27726,7 +28380,7 @@ ${languageDirective}
27726
28380
  ${buildHeadingDirective("EXT", slcMarkdown, targetLang)}`;
27727
28381
  const rawExt = await runCurriculumAIInference(
27728
28382
  [{ role: "user", content: extPrompt }],
27729
- satelliteContext,
28383
+ satelliteContextFor("EXT"),
27730
28384
  runnerOptions,
27731
28385
  (chunk, type) => {
27732
28386
  options.onProgress?.("@activity", chunk, { type: type || "chunk", artifactType: "EXT" });
@@ -30826,6 +31480,75 @@ var DeterministicStructuralLinter = class {
30826
31480
  strengths
30827
31481
  };
30828
31482
  }
31483
+ /**
31484
+ * P50: Validates macro-pedagogical invariants across a CurriculumPlan.
31485
+ * Checks:
31486
+ * 1. ZPD violations without self-healing recap (NO_ZPD_BRIDGE)
31487
+ * 2. Walking Skeleton existence (Unit 1 delivers working software)
31488
+ * 3. Inter-unit Mastery Gate presence
31489
+ */
31490
+ static lintPlan(plan) {
31491
+ const findings = [];
31492
+ const strengths = [];
31493
+ let score = 100;
31494
+ for (const s of plan.sessions) {
31495
+ if (s.zpd_status?.verdict === "TOO_MANY_NEW") {
31496
+ score -= 10;
31497
+ findings.push({
31498
+ id: `PLAN_TOO_MANY_NEW_${s.id}`,
31499
+ dimension: "CONSTRUCTIVE_ALIGNMENT",
31500
+ severity: "MINOR",
31501
+ title: `Cognitive Overload in Session ${s.id}`,
31502
+ description: s.zpd_status.issues.join("; ") || `Session introduces too many new concepts/keywords, exceeding ZPD capacity.`,
31503
+ remediationAdvice: `Distribute new concepts across multiple sessions or introduce via scaffolding.`,
31504
+ affectedElement: s.id
31505
+ });
31506
+ } else if (s.zpd_status?.verdict === "NO_ZPD_BRIDGE") {
31507
+ const hasRecap = s.prerequisite_decisions.some((d) => d.decision === "recap_in_lesson");
31508
+ if (!hasRecap) {
31509
+ score -= 15;
31510
+ findings.push({
31511
+ id: `PLAN_NO_ZPD_BRIDGE_${s.id}`,
31512
+ dimension: "CONSTRUCTIVE_ALIGNMENT",
31513
+ severity: "MAJOR",
31514
+ title: `Unhealed ZPD Bridge in Session ${s.id}`,
31515
+ description: `Session introduces new concepts with 0 known concept bridges, and lacks recap scaffolding.`,
31516
+ remediationAdvice: `Add a recap_in_lesson prerequisite decision to anchor new learning in prior concepts.`,
31517
+ affectedElement: s.id
31518
+ });
31519
+ }
31520
+ }
31521
+ }
31522
+ if (!plan.walking_skeleton || plan.walking_skeleton.epitome_deliverables.length === 0) {
31523
+ score -= 10;
31524
+ findings.push({
31525
+ id: "PLAN_MISSING_WALKING_SKELETON",
31526
+ dimension: "CONSTRUCTIVE_ALIGNMENT",
31527
+ severity: "MINOR",
31528
+ title: "Missing Walking Skeleton Definition",
31529
+ description: "Plan does not declare an Epitome (walking skeleton) in Unit 1.",
31530
+ remediationAdvice: "Ensure Unit 1 defines minimal end-to-end deliverables."
31531
+ });
31532
+ } else {
31533
+ strengths.push(`Unit ${plan.walking_skeleton.epitome_unit_id} establishes an end-to-end Walking Skeleton (Epitome).`);
31534
+ }
31535
+ if (plan.units.length > 1 && (!plan.mastery_gates || plan.mastery_gates.length === 0)) {
31536
+ score -= 10;
31537
+ findings.push({
31538
+ id: "PLAN_MISSING_MASTERY_GATES",
31539
+ dimension: "5E_INSTRUCTIONAL_FIDELITY",
31540
+ severity: "MINOR",
31541
+ title: "Missing Inter-Unit Mastery Gates",
31542
+ description: "Multi-unit course lacks formal transition gates between units.",
31543
+ remediationAdvice: "Define exit criteria and remediation sprints for each inter-unit boundary."
31544
+ });
31545
+ } else if (plan.mastery_gates && plan.mastery_gates.length > 0) {
31546
+ strengths.push(`${plan.mastery_gates.length} inter-unit Mastery Gates defined with remediation protocols.`);
31547
+ }
31548
+ score = Math.max(0, Math.min(100, score));
31549
+ const passed = score >= 80 && !findings.some((f) => f.severity === "CRITICAL");
31550
+ return { passed, score, findings, strengths };
31551
+ }
30829
31552
  };
30830
31553
 
30831
31554
  // src/evaluators/academicAuditor.ts
@@ -32496,6 +33219,8 @@ exports.CodeLabSchema = CodeLabSchema;
32496
33219
  exports.CodeSnippetSchema = CodeSnippetSchema;
32497
33220
  exports.CompetencyRubricRowSchema = CompetencyRubricRowSchema;
32498
33221
  exports.ComputationalThinkingSchema = ComputationalThinkingSchema;
33222
+ exports.ConceptPrerequisiteEdgeSchema = ConceptPrerequisiteEdgeSchema;
33223
+ exports.ConceptSpiralEncounterSchema = ConceptSpiralEncounterSchema;
32499
33224
  exports.ConstructiveAlignmentEvaluator = ConstructiveAlignmentEvaluator;
32500
33225
  exports.CoreConceptSchema = CoreConceptSchema;
32501
33226
  exports.CourseObjectiveSchema = CourseObjectiveSchema;
@@ -32570,6 +33295,7 @@ exports.LessonSectionSchema = LessonSectionSchema;
32570
33295
  exports.LocalWorkspaceManager = LocalWorkspaceManager;
32571
33296
  exports.MappingKindSchema = MappingKindSchema;
32572
33297
  exports.MarpSlideSchema = MarpSlideSchema;
33298
+ exports.MasteryGateSchema = MasteryGateSchema;
32573
33299
  exports.MediaError = MediaError;
32574
33300
  exports.MediaLedger = MediaLedger;
32575
33301
  exports.MentorCheatsheetSchema = MentorCheatsheetSchema;
@@ -32623,6 +33349,7 @@ exports.SelfLabSchema = SelfLabSchema;
32623
33349
  exports.SelfPacedBundleSchema = SelfPacedBundleSchema;
32624
33350
  exports.SelfPacedChallengeSchema = SelfPacedChallengeSchema;
32625
33351
  exports.SessionPlanSchema = SessionPlanSchema;
33352
+ exports.SessionZpdStatusSchema = SessionZpdStatusSchema;
32626
33353
  exports.SilverTierSchema = SilverTierSchema;
32627
33354
  exports.SlideDeckSchema = SlideDeckSchema;
32628
33355
  exports.StandardizedTermRowSchema = StandardizedTermRowSchema;
@@ -32647,20 +33374,24 @@ exports.TroubleshootingRowSchema = TroubleshootingRowSchema;
32647
33374
  exports.UnitPlanSchema = UnitPlanSchema;
32648
33375
  exports.UserJourneySchema = UserJourneySchema;
32649
33376
  exports.WORKSHEET_TEMPLATE = WORKSHEET_TEMPLATE;
33377
+ exports.WalkingSkeletonSchema = WalkingSkeletonSchema;
32650
33378
  exports.WorksheetItemSchema = WorksheetItemSchema;
32651
33379
  exports.WorksheetItemTypeEnum = WorksheetItemTypeEnum;
32652
33380
  exports.WorksheetPartSchema = WorksheetPartSchema;
32653
33381
  exports.WorksheetSchema = WorksheetSchema;
33382
+ exports.ZpdVerdictSchema = ZpdVerdictSchema;
32654
33383
  exports.activityTools = activityTools;
32655
33384
  exports.analystTools = analystTools;
32656
33385
  exports.analyzeProjectCreationIntent = analyzeProjectCreationIntent;
32657
33386
  exports.assertAcyclic = assertAcyclic;
32658
33387
  exports.assessorTools = assessorTools;
33388
+ exports.assignDepths = assignDepths;
32659
33389
  exports.atomicWriteFileSync = atomicWriteFileSync;
32660
33390
  exports.auditCurriculumQualityFlow = auditCurriculumQualityFlow;
32661
33391
  exports.auditQualityReport = auditQualityReport;
32662
33392
  exports.buildActivityPrompt = buildActivityPrompt;
32663
33393
  exports.buildCodeLabPrompt = buildCodeLabPrompt;
33394
+ exports.buildConceptPrerequisites = buildConceptPrerequisites;
32664
33395
  exports.buildCurriculumContext = buildCurriculumContext;
32665
33396
  exports.buildCurriculumPlan = buildCurriculumPlan;
32666
33397
  exports.buildDeliveryPackages = buildDeliveryPackages;
@@ -32678,7 +33409,9 @@ exports.buildLanguageDirective = buildLanguageDirective;
32678
33409
  exports.buildLessonExcerpt = buildLessonExcerpt;
32679
33410
  exports.buildLessonMasterPrompt = buildLessonMasterPrompt;
32680
33411
  exports.buildMarpMarkdownSlidePrompt = buildMarpMarkdownSlidePrompt;
33412
+ exports.buildMasteryGates = buildMasteryGates;
32681
33413
  exports.buildProjectInstructionPrompt = buildProjectInstructionPrompt;
33414
+ exports.buildSatelliteContext = buildSatelliteContext;
32682
33415
  exports.buildSectionAwareExcerpt = buildSectionAwareExcerpt;
32683
33416
  exports.buildSelfLabPrompt = buildSelfLabPrompt;
32684
33417
  exports.buildSessionSliceContext = buildSessionSliceContext;
@@ -32687,8 +33420,11 @@ exports.buildStandardStackMarkdown = buildStandardStackMarkdown;
32687
33420
  exports.buildStandardsContext = buildStandardsContext;
32688
33421
  exports.buildStandardsContextBlock = buildStandardsContextBlock;
32689
33422
  exports.buildTeacherGuidePrompt = buildTeacherGuidePrompt;
33423
+ exports.buildWalkingSkeleton = buildWalkingSkeleton;
32690
33424
  exports.buildWorksheetPrompt = buildWorksheetPrompt;
33425
+ exports.checkSessionZpd = checkSessionZpd;
32691
33426
  exports.closeTruncatedJson = closeTruncatedJson;
33427
+ exports.computeConceptSpiralProgression = computeConceptSpiralProgression;
32692
33428
  exports.computeContentHash = computeContentHash;
32693
33429
  exports.computePackingSpec = computePackingSpec;
32694
33430
  exports.conductPreliminaryResearch = conductPreliminaryResearch;
@@ -32704,14 +33440,18 @@ exports.createStreamChunkExtractor = createStreamChunkExtractor;
32704
33440
  exports.curateMediaLedger = curateMediaLedger;
32705
33441
  exports.designerTools = designerTools;
32706
33442
  exports.detectProjectPedagogy = detectProjectPedagogy;
33443
+ exports.dropCyclicConceptEdges = dropCyclicConceptEdges;
32707
33444
  exports.emitUsage = emitUsage;
32708
33445
  exports.ensureExpositionForLesson = ensureExpositionForLesson;
32709
33446
  exports.ensureKnowledgeExposition = ensureKnowledgeExposition;
33447
+ exports.evaluateActivityAlignment = evaluateActivityAlignment;
32710
33448
  exports.evaluateStandardsCoverage = evaluateStandardsCoverage;
32711
33449
  exports.executeCurriculumCommand = executeCurriculumCommand;
32712
33450
  exports.executeSlideProductionWorkflow = executeSlideProductionWorkflow;
32713
33451
  exports.exportAllProjectQuizzes = exportAllProjectQuizzes;
32714
33452
  exports.expositionCacheKey = expositionCacheKey;
33453
+ exports.extractActivityContractRows = extractActivityContractRows;
33454
+ exports.extractAssessmentMap = extractAssessmentMap;
32715
33455
  exports.extractCurriculumHorizon = extractCurriculumHorizon;
32716
33456
  exports.extractJsonArray = extractJsonArray;
32717
33457
  exports.extractScopeSequenceRows = extractScopeSequenceRows;
@@ -32721,6 +33461,7 @@ exports.extractStandardRefs = extractStandardRefs;
32721
33461
  exports.extractStreamChunk = extractStreamChunk;
32722
33462
  exports.extractSymbolLedger = extractSymbolLedger;
32723
33463
  exports.extractThoughtAndContent = extractThoughtAndContent;
33464
+ exports.extractTieredScaffoldingBlock = extractTieredScaffoldingBlock;
32724
33465
  exports.findStandardStatement = findStandardStatement;
32725
33466
  exports.formatQuizzesToCsv = formatQuizzesToCsv;
32726
33467
  exports.fulfillMediaLedger = fulfillMediaLedger;
@@ -32760,6 +33501,7 @@ exports.importRoadmapToProject = importRoadmapToProject;
32760
33501
  exports.ingestProjectGraphIntoProject = ingestProjectGraphIntoProject;
32761
33502
  exports.initializeProjectInStorage = initializeProjectInStorage;
32762
33503
  exports.injectMediaIntoMarkdown = injectMediaIntoMarkdown;
33504
+ exports.isActivityAlignmentLogOnly = isActivityAlignmentLogOnly;
32763
33505
  exports.isExpositionFresh = isExpositionFresh;
32764
33506
  exports.isModelAllowed = isModelAllowed;
32765
33507
  exports.isProviderEnabled = isProviderEnabled;