@thanh01.pmt/curriculum-kit 1.4.23 → 1.4.24

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.cjs CHANGED
@@ -26203,7 +26203,22 @@ async function produceSingleLesson(storage, projectId, lessonId, options = {}) {
26203
26203
  const framework = await storage.readSotDocument(projectId, "CURRICULUM_FRAMEWORK.md") || "";
26204
26204
  const styleGuide = await storage.readSotDocument(projectId, "CONTENT_STYLE_GUIDE.md") || "";
26205
26205
  const refPack = await storage.readSotDocument(projectId, "REFERENCE_PACK.md") || "";
26206
+ let currentPlanHash;
26207
+ try {
26208
+ const planRaw0 = await storage.readArtifact(projectId, "_sot/CURRICULUM_PLAN.json");
26209
+ if (planRaw0) currentPlanHash = JSON.parse(planRaw0).plan_hash;
26210
+ } catch {
26211
+ }
26206
26212
  await storage.readSotDocument(projectId, "LEARNER_PROFILE.md") || "";
26213
+ const isStaleVsPlan = (content) => {
26214
+ if (!content || !currentPlanHash) return false;
26215
+ const m = content.match(/plan_hash:\s*"([0-9a-f]+)"/);
26216
+ return !!m && m[1] !== currentPlanHash;
26217
+ };
26218
+ const stampPlanHash = (content) => {
26219
+ if (!currentPlanHash || content.includes('plan_hash: "' + currentPlanHash + '"')) return content;
26220
+ return content + '\n---\nplan_hash: "' + currentPlanHash + '"\n- **Plan reference:** ' + currentPlanHash + "\n";
26221
+ };
26207
26222
  const slcMarkdown = options.sectionLanguageContract || await storage.readSotDocument(projectId, "SECTION_LANGUAGE_CONTRACT.md") || "";
26208
26223
  let targetLang = options.targetLanguage;
26209
26224
  if (!targetLang) {
@@ -26414,7 +26429,10 @@ ${sg}${standardsBlock}${glossaryBlock}${buildGroundTruthBlock()}${sessionSliceBl
26414
26429
  const lessonRelPath = `_content/${unitCode}/LESSON_${lessonCode}.md`;
26415
26430
  const existingLessonContent = await storage.readArtifact(projectId, lessonRelPath);
26416
26431
  let lessonContent = existingLessonContent || "";
26417
- if (artifactScope.includes("LESSON") && (!existingLessonContent || force)) {
26432
+ if (artifactScope.includes("LESSON") && (!existingLessonContent || force || isStaleVsPlan(existingLessonContent))) {
26433
+ if (existingLessonContent && !force && isStaleVsPlan(existingLessonContent)) {
26434
+ onProgress?.("@content", `[STALENESS] LESSON_${lessonCode} was generated under a different plan_hash \u2014 regenerating against the current plan.`);
26435
+ }
26418
26436
  onProgress?.("@content", `[1/4] Authoring Master Lesson Plan (${pedagogyLabel}): \`LESSON_${lessonCode}.md\` (${lessonTitle})...`);
26419
26437
  const dateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
26420
26438
  const mediaDirective = options.mediaPolicy && options.mediaPolicy.maxGenImagesPerArtifact > 0 ? `
@@ -26485,7 +26503,7 @@ Fix ALL issues above and output the complete corrected document.` }],
26485
26503
  } else {
26486
26504
  lessonContent = lintReport.autoFixedContent || rawLesson;
26487
26505
  }
26488
- await storage.saveArtifact(projectId, lessonRelPath, lessonContent);
26506
+ await storage.saveArtifact(projectId, lessonRelPath, stampPlanHash(lessonContent));
26489
26507
  producedArtifacts.push(`LESSON_${lessonCode}.md`);
26490
26508
  }
26491
26509
  const contentHash = lessonContent ? computeContentHash(lessonContent) : void 0;
@@ -26830,7 +26848,8 @@ ${lessonExcerpt}${symbolLedgerBlock}`;
26830
26848
  });
26831
26849
  };
26832
26850
  const actRelPath = `_content/${unitCode}/ACT_${lessonCode}.md`;
26833
- if (artifactScope.includes("ACT") && (!await storage.readArtifact(projectId, actRelPath) || force)) {
26851
+ const existingAct = await storage.readArtifact(projectId, actRelPath);
26852
+ if (artifactScope.includes("ACT") && (!existingAct || force || isStaleVsPlan(existingAct))) {
26834
26853
  onProgress?.("@activity", `[2/4] Authoring Hands-on Lab: \`ACT_${lessonCode}.md\`...`);
26835
26854
  const actDateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
26836
26855
  const actPrompt = `You are @activity (Lead Hands-on STEM & Laboratory Exercise Designer).
@@ -26890,7 +26909,7 @@ ${buildHeadingDirective("ACT", slcMarkdown, targetLang)}`;
26890
26909
  rawError: rawAct || "Empty AI response"
26891
26910
  });
26892
26911
  }
26893
- await storage.saveArtifact(projectId, actRelPath, rawAct);
26912
+ await storage.saveArtifact(projectId, actRelPath, stampPlanHash(rawAct));
26894
26913
  await storage.updateArtifactState(projectId, lessonCode, "ACT", {
26895
26914
  state: "completed",
26896
26915
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -26900,7 +26919,8 @@ ${buildHeadingDirective("ACT", slcMarkdown, targetLang)}`;
26900
26919
  await judgeSat("ACT", rawAct);
26901
26920
  }
26902
26921
  const quizRelPath = `_content/${unitCode}/QUIZ_${lessonCode}.md`;
26903
- if (artifactScope.includes("QUIZ") && (!await storage.readArtifact(projectId, quizRelPath) || force)) {
26922
+ const existingQuiz = await storage.readArtifact(projectId, quizRelPath);
26923
+ if (artifactScope.includes("QUIZ") && (!existingQuiz || force || isStaleVsPlan(existingQuiz))) {
26904
26924
  onProgress?.("@assessor", `[3/4] Authoring Assessment Bank: \`QUIZ_${lessonCode}.md\`...`);
26905
26925
  const quizDateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
26906
26926
  const quizPrompt = `You are @assessor (Lead Psychometrician & Competency Assessment Specialist).
@@ -26954,7 +26974,7 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
26954
26974
  rawError: rawQuiz || "Empty AI response"
26955
26975
  });
26956
26976
  }
26957
- await storage.saveArtifact(projectId, quizRelPath, rawQuiz);
26977
+ await storage.saveArtifact(projectId, quizRelPath, stampPlanHash(rawQuiz));
26958
26978
  await storage.updateArtifactState(projectId, lessonCode, "QUIZ", {
26959
26979
  state: "completed",
26960
26980
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -26964,7 +26984,8 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
26964
26984
  await judgeSat("QUIZ", rawQuiz);
26965
26985
  }
26966
26986
  const slideRelPath = `_content/${unitCode}/SLIDE_${lessonCode}.md`;
26967
- if (artifactScope.includes("SLIDE") && (!await storage.readArtifact(projectId, slideRelPath) || force)) {
26987
+ const existingSlide = await storage.readArtifact(projectId, slideRelPath);
26988
+ if (artifactScope.includes("SLIDE") && (!existingSlide || force || isStaleVsPlan(existingSlide))) {
26968
26989
  onProgress?.("@illustrator", `[4/4] Authoring Presentation Slide Deck: \`SLIDE_${lessonCode}.md\`...`);
26969
26990
  const isHtmlEngine = options.slidesEngine !== "marp";
26970
26991
  if (isHtmlEngine) {
@@ -26998,7 +27019,7 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
26998
27019
  let deckJson = workflowResult.deckJson;
26999
27020
  const markdownWrapper = workflowResult.markdownWrapper;
27000
27021
  let validation = presentationKitAi?.validateHtmlSlideDeck ? presentationKitAi.validateHtmlSlideDeck(deckJson, true) : deckJson && Array.isArray(deckJson.slides) ? { valid: true, score: 95, issues: [], slideCount: deckJson.slides.length} : { valid: false, score: 0, issues: [{ message: "Malformed JSON slide deck" }]};
27001
- await storage.saveArtifact(projectId, slideRelPath, markdownWrapper);
27022
+ await storage.saveArtifact(projectId, slideRelPath, stampPlanHash(markdownWrapper));
27002
27023
  producedArtifacts.push(`SLIDE_${lessonCode}.md`);
27003
27024
  if (deckJson) {
27004
27025
  const deckJsonStr = JSON.stringify(deckJson, null, 2);
@@ -27089,7 +27110,7 @@ footer: "Curriculum OS \u2022 ${lessonCode} (${pedagogyLabel})"
27089
27110
  rawError: rawSlide || "Empty AI response"
27090
27111
  });
27091
27112
  }
27092
- await storage.saveArtifact(projectId, slideRelPath, rawSlide);
27113
+ await storage.saveArtifact(projectId, slideRelPath, stampPlanHash(rawSlide));
27093
27114
  await storage.updateArtifactState(projectId, lessonCode, "SLIDE", {
27094
27115
  state: "completed",
27095
27116
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -27100,7 +27121,8 @@ footer: "Curriculum OS \u2022 ${lessonCode} (${pedagogyLabel})"
27100
27121
  }
27101
27122
  }
27102
27123
  const guideRelPath = `_content/${unitCode}/GUIDE_${lessonCode}.md`;
27103
- if (artifactScope.includes("GUIDE") && (!await storage.readArtifact(projectId, guideRelPath) || force)) {
27124
+ const existingGuide = await storage.readArtifact(projectId, guideRelPath);
27125
+ if (artifactScope.includes("GUIDE") && (!existingGuide || force || isStaleVsPlan(existingGuide))) {
27104
27126
  onProgress?.("@content", `Authoring Teacher Guide: \`GUIDE_${lessonCode}.md\`...`, { artifactType: "GUIDE" });
27105
27127
  const guideDateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
27106
27128
  const guidePrompt = `You are @content (Senior Curriculum Developer & Teacher Facilitation Specialist).
@@ -27140,7 +27162,7 @@ ${buildHeadingDirective("GUIDE", slcMarkdown, targetLang)}`;
27140
27162
  }
27141
27163
  );
27142
27164
  if (rawGuide && !rawGuide.startsWith("\u26A0\uFE0F") && rawGuide.length >= 200) {
27143
- await storage.saveArtifact(projectId, guideRelPath, rawGuide);
27165
+ await storage.saveArtifact(projectId, guideRelPath, stampPlanHash(rawGuide));
27144
27166
  await storage.updateArtifactState(projectId, lessonCode, "GUIDE", {
27145
27167
  state: "completed",
27146
27168
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -27151,7 +27173,8 @@ ${buildHeadingDirective("GUIDE", slcMarkdown, targetLang)}`;
27151
27173
  }
27152
27174
  }
27153
27175
  const handoutRelPath = `_content/${unitCode}/HANDOUT_${lessonCode}.md`;
27154
- if (artifactScope.includes("HANDOUT") && (!await storage.readArtifact(projectId, handoutRelPath) || force)) {
27176
+ const existingHandout = await storage.readArtifact(projectId, handoutRelPath);
27177
+ if (artifactScope.includes("HANDOUT") && (!existingHandout || force || isStaleVsPlan(existingHandout))) {
27155
27178
  onProgress?.("@content", `Authoring Student Handout: \`HANDOUT_${lessonCode}.md\`...`, { artifactType: "HANDOUT" });
27156
27179
  const handoutDateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
27157
27180
  const handoutPrompt = `You are @content (Educational Content Specialist).
@@ -27191,7 +27214,7 @@ ${buildHeadingDirective("HANDOUT", slcMarkdown, targetLang)}`;
27191
27214
  }
27192
27215
  );
27193
27216
  if (rawHandout && !rawHandout.startsWith("\u26A0\uFE0F") && rawHandout.length >= 200) {
27194
- await storage.saveArtifact(projectId, handoutRelPath, rawHandout);
27217
+ await storage.saveArtifact(projectId, handoutRelPath, stampPlanHash(rawHandout));
27195
27218
  await storage.updateArtifactState(projectId, lessonCode, "HANDOUT", {
27196
27219
  state: "completed",
27197
27220
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -27202,7 +27225,8 @@ ${buildHeadingDirective("HANDOUT", slcMarkdown, targetLang)}`;
27202
27225
  }
27203
27226
  }
27204
27227
  const wksRelPath = `_content/${unitCode}/WKS_${lessonCode}.md`;
27205
- if (artifactScope.includes("WKS") && (!await storage.readArtifact(projectId, wksRelPath) || force)) {
27228
+ const existingWks = await storage.readArtifact(projectId, wksRelPath);
27229
+ if (artifactScope.includes("WKS") && (!existingWks || force || isStaleVsPlan(existingWks))) {
27206
27230
  onProgress?.("@content", `Authoring Student Worksheet: \`WKS_${lessonCode}.md\`...`, { artifactType: "WKS" });
27207
27231
  const wksDateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
27208
27232
  const wksPrompt = `You are @content (Instructional Activity & Worksheet Designer).
@@ -27246,7 +27270,7 @@ ${buildHeadingDirective("WKS", slcMarkdown, targetLang)}`;
27246
27270
  }
27247
27271
  );
27248
27272
  if (rawWks && !rawWks.startsWith("\u26A0\uFE0F") && rawWks.length >= 200) {
27249
- await storage.saveArtifact(projectId, wksRelPath, rawWks);
27273
+ await storage.saveArtifact(projectId, wksRelPath, stampPlanHash(rawWks));
27250
27274
  await storage.updateArtifactState(projectId, lessonCode, "WKS", {
27251
27275
  state: "completed",
27252
27276
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -27257,7 +27281,8 @@ ${buildHeadingDirective("WKS", slcMarkdown, targetLang)}`;
27257
27281
  }
27258
27282
  }
27259
27283
  const codeRelPath = `_content/${unitCode}/CODE_${lessonCode}.md`;
27260
- if ((artifactScope.includes("CODE") || artifactScope.includes("CODE_LAB")) && (!await storage.readArtifact(projectId, codeRelPath) || force)) {
27284
+ const existingCode = await storage.readArtifact(projectId, codeRelPath);
27285
+ if ((artifactScope.includes("CODE") || artifactScope.includes("CODE_LAB")) && (!existingCode || force || isStaleVsPlan(existingCode))) {
27261
27286
  onProgress?.("@activity", `Authoring Executable Code Lab: \`CODE_${lessonCode}.md\`...`, { artifactType: "CODE" });
27262
27287
  const codeDateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
27263
27288
  const codePrompt = `You are @activity & @tech_sme (Lead Software Engineer & Technical SME).
@@ -27305,7 +27330,7 @@ ${buildHeadingDirective("CODE", slcMarkdown, targetLang)}`;
27305
27330
  }
27306
27331
  );
27307
27332
  if (rawCode && !rawCode.startsWith("\u26A0\uFE0F") && rawCode.length >= 200) {
27308
- await storage.saveArtifact(projectId, codeRelPath, rawCode);
27333
+ await storage.saveArtifact(projectId, codeRelPath, stampPlanHash(rawCode));
27309
27334
  await storage.updateArtifactState(projectId, lessonCode, "CODE", {
27310
27335
  state: "completed",
27311
27336
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -27321,7 +27346,8 @@ ${buildHeadingDirective("CODE", slcMarkdown, targetLang)}`;
27321
27346
  }
27322
27347
  }
27323
27348
  const extRelPath = `_content/${unitCode}/EXT_${lessonCode}.md`;
27324
- if (artifactScope.includes("EXT") && (!await storage.readArtifact(projectId, extRelPath) || force)) {
27349
+ const existingExt = await storage.readArtifact(projectId, extRelPath);
27350
+ if (artifactScope.includes("EXT") && (!existingExt || force || isStaleVsPlan(existingExt))) {
27325
27351
  onProgress?.("@activity", `Authoring Advanced Extension Challenge: \`EXT_${lessonCode}.md\`...`, { artifactType: "EXT" });
27326
27352
  const extDateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
27327
27353
  const isVi = targetLang === "vi";
@@ -27391,7 +27417,7 @@ ${buildHeadingDirective("EXT", slcMarkdown, targetLang)}`;
27391
27417
  }
27392
27418
  );
27393
27419
  if (rawExt && !rawExt.startsWith("\u26A0\uFE0F") && rawExt.length >= 200) {
27394
- await storage.saveArtifact(projectId, extRelPath, rawExt);
27420
+ await storage.saveArtifact(projectId, extRelPath, stampPlanHash(rawExt));
27395
27421
  await storage.updateArtifactState(projectId, lessonCode, "EXT", {
27396
27422
  state: "completed",
27397
27423
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),