@thanh01.pmt/curriculum-kit 1.3.0 → 1.4.1

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
@@ -9841,7 +9841,9 @@ function buildSectionAwareExcerpt(markdown, opts = {}) {
9841
9841
  };
9842
9842
  }
9843
9843
  const slcMap = normalizeSlcContract(opts.sectionLanguageContract);
9844
- const sections = parseMarkdownSections(source, slcMap, opts.artifactType);
9844
+ const effectiveArtifactType = opts.artifactType || deriveArtifactTypeFromFileName(opts.fileName);
9845
+ const sections = parseMarkdownSections(source, slcMap, effectiveArtifactType);
9846
+ const metaBlock = buildMetaBlock(source, effectiveArtifactType);
9845
9847
  if (sections.length === 0) {
9846
9848
  issues.push("parse:no-sections");
9847
9849
  const excerpt2 = truncateByBudget(source, budget);
@@ -9875,12 +9877,13 @@ function buildSectionAwareExcerpt(markdown, opts = {}) {
9875
9877
  const block = `## ${heading}
9876
9878
 
9877
9879
  ${trimmedBody}`;
9878
- if (used + block.length > budget && blocks.length > 0) return false;
9880
+ if (used + block.length > budget && used > 0) return false;
9879
9881
  blocks.push(block);
9880
9882
  used += block.length;
9881
9883
  includedSections.push(key);
9882
9884
  return true;
9883
9885
  };
9886
+ blocks.push(metaBlock);
9884
9887
  let sectionAware = false;
9885
9888
  for (const key of priorities) {
9886
9889
  const sec = byCanonical.get(key);
@@ -9897,6 +9900,9 @@ ${trimmedBody}`;
9897
9900
  if (used >= budget) break;
9898
9901
  if (!tryAdd(sec.cleanTitle, sec.body, sec.cleanTitle)) break;
9899
9902
  }
9903
+ if (includedSections.length === 0 && unmatched.length > 0) {
9904
+ issues.push("parse:no-canonical-resolution");
9905
+ }
9900
9906
  if (priorities.length > 0 && !priorities.some((k) => includedSections.includes(k))) {
9901
9907
  issues.push("parse:no-priority-resolution");
9902
9908
  }
@@ -9905,14 +9911,14 @@ ${trimmedBody}`;
9905
9911
  excerpt = truncateByBudget(excerpt, budget);
9906
9912
  }
9907
9913
  const effectiveMinChars = Math.min(minContentChars, source.length);
9908
- if (excerpt.length < effectiveMinChars) {
9914
+ if (excerpt.length - metaBlock.length < effectiveMinChars) {
9909
9915
  issues.push("content:insufficient");
9910
9916
  }
9911
- const verified = !issues.includes("parse:no-priority-resolution") && excerpt.length >= effectiveMinChars;
9917
+ const verified = !issues.includes("parse:no-priority-resolution") && !issues.includes("parse:no-canonical-resolution") && excerpt.length - metaBlock.length >= effectiveMinChars;
9912
9918
  return {
9913
9919
  excerpt,
9914
9920
  verified,
9915
- sectionAware: sectionAware || priorities.length === 0 && sections.length > 0,
9921
+ sectionAware: sectionAware || unmatched.length === 0,
9916
9922
  includedSections,
9917
9923
  issues,
9918
9924
  tokenEstimate: Math.round(excerpt.length / 4)
@@ -10028,6 +10034,26 @@ function truncateByBudget(source, budget) {
10028
10034
  if (fenceCount % 2 === 1) cut += "\n```";
10029
10035
  return cut;
10030
10036
  }
10037
+ function buildMetaBlock(source, artifactType) {
10038
+ const fm = source.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/);
10039
+ const pick = (key) => {
10040
+ if (!fm) return "";
10041
+ const m = fm[1].match(new RegExp(`^${key}:\\s*"?([^"\\n]+)"?`, "m"));
10042
+ return (m?.[1] || "").trim();
10043
+ };
10044
+ const id = pick("id");
10045
+ const title = pick("title");
10046
+ const type = pick("type") || artifactType || "";
10047
+ return `<!-- SOURCE: ${type || "ARTIFACT"} | ${id || "unknown"}${title ? ` \u2014 ${title}` : ""} (section-aware excerpt; canonical knowledge, do not contradict) -->`;
10048
+ }
10049
+ function deriveArtifactTypeFromFileName(fileName) {
10050
+ if (!fileName) return void 0;
10051
+ const base = fileName.replace(/\.md$/i, "");
10052
+ const multiWord = base.match(/^(KNOWLEDGE_EXPOSITION|SECTION_LANGUAGE_CONTRACT|CONTENT_STYLE_GUIDE|CURRICULUM_FRAMEWORK|LEARNER_PROFILE|PROJECT_BRIEF|REFERENCE_PACK|CURRICULUM_PLAN)/i);
10053
+ if (multiWord) return multiWord[1].toUpperCase();
10054
+ const token = base.match(/^([A-Z][A-Z0-9_]*?)(?=_|$)/);
10055
+ return token ? token[1] : void 0;
10056
+ }
10031
10057
 
10032
10058
  // src/services/knowledgeExpositionService.ts
10033
10059
  var EXPOSITION_REL = (lessonCode) => "_sot/expositions/" + lessonCode + ".md";
@@ -10176,7 +10202,7 @@ async function ensureExpositionForLesson(storage, projectId, lessonCode, options
10176
10202
  storage,
10177
10203
  gates: options.gates
10178
10204
  });
10179
- return { relPath: result.relPath, context: buildExpositionContext(result.content), reused: result.reused };
10205
+ return { relPath: result.relPath, context: buildExpositionContext(result.content), reused: result.reused, rawContent: result.content };
10180
10206
  }
10181
10207
  init_errors();
10182
10208
  var asArr = (v) => Array.isArray(v) ? v : [];
@@ -11887,6 +11913,8 @@ async function produceSingleLesson(storage, projectId, lessonId, options = {}) {
11887
11913
  targetLang = targetLang || "vi";
11888
11914
  const languageDirective = buildLanguageDirective(targetLang);
11889
11915
  let expositionContext = "";
11916
+ let expositionInjection;
11917
+ let lessonInjection;
11890
11918
  let sessionSliceContext = "";
11891
11919
  try {
11892
11920
  const exposition = await ensureExpositionForLesson(storage, projectId, lessonCode, {
@@ -11894,7 +11922,18 @@ async function produceSingleLesson(storage, projectId, lessonId, options = {}) {
11894
11922
  force: options.force
11895
11923
  });
11896
11924
  if (exposition) {
11897
- expositionContext = exposition.context;
11925
+ const expoExcerpt = buildExpositionExcerpt(exposition.rawContent || exposition.context, {
11926
+ budget: 6e3,
11927
+ sectionLanguageContract: slcMarkdown,
11928
+ artifactType: "KNOWLEDGE_EXPOSITION"
11929
+ });
11930
+ expositionContext = expoExcerpt.excerpt || exposition.context;
11931
+ expositionInjection = {
11932
+ source: "KNOWLEDGE_EXPOSITION",
11933
+ verified: expoExcerpt.verified,
11934
+ sectionAware: expoExcerpt.sectionAware,
11935
+ issues: expoExcerpt.issues
11936
+ };
11898
11937
  onProgress?.("@content", `[EXPOSITION] ${exposition.reused ? "Reused" : "Generated"} \`\${exposition.relPath}\` (plan-driven knowledge source)...`);
11899
11938
  } else {
11900
11939
  onProgress?.("@content", "[EXPOSITION] No CURRICULUM_PLAN.json \u2014 running legacy knowledge flow (no plan-driven exposition).");
@@ -12134,6 +12173,7 @@ ${yamlBlock.trim()}
12134
12173
  };
12135
12174
  var injectYamlReviewMetadata = injectYamlReviewMetadata2;
12136
12175
  const judgeObjectives = parseLessonObjectives(lessonContent, concept, bloomLevel);
12176
+ const judgeExpositionExcerpt = expositionContext ? buildExpositionExcerpt(expositionContext, { budget: 3e3, sectionLanguageContract: slcMarkdown, artifactType: "KNOWLEDGE_EXPOSITION" }).excerpt : void 0;
12137
12177
  const judgeResult = await LLMJudgeEngine.evaluateArtifactWithLLM({
12138
12178
  targetArtifactType: "LESSON",
12139
12179
  lessonId: lessonCode,
@@ -12145,7 +12185,7 @@ ${yamlBlock.trim()}
12145
12185
  title: lessonTitle,
12146
12186
  frameworkExcerpt: framework ? buildFrameworkExcerptForLesson(framework, lessonId) : void 0,
12147
12187
  styleGuideExcerpt: effectiveStyleGuide || void 0,
12148
- canonicalExposition: expositionContext ? expositionContext.slice(0, 3e3) : void 0
12188
+ canonicalExposition: judgeExpositionExcerpt
12149
12189
  }),
12150
12190
  standardStatements: Object.keys(standardStatements).length > 0 ? standardStatements : void 0
12151
12191
  });
@@ -12200,6 +12240,16 @@ ${currentContent}` }],
12200
12240
  options.onProgress?.("@repair", chunk, { type: type || "content", artifactType: "LESSON" });
12201
12241
  }
12202
12242
  );
12243
+ const repairedExcerptCheck = buildSectionAwareExcerpt(repairedRaw, {
12244
+ priorities: DEFAULT_LESSON_PRIORITIES,
12245
+ budget: 12e3,
12246
+ sectionLanguageContract: slcMarkdown,
12247
+ artifactType: "LESSON"
12248
+ });
12249
+ if (repairedExcerptCheck.issues.includes("parse:no-priority-resolution")) {
12250
+ onProgress?.("@repair", `\u26A0\uFE0F Repair round ${round} lost mandatory section structure (no priority section resolved) \u2014 rejecting repaired draft, retaining previous`);
12251
+ break;
12252
+ }
12203
12253
  if (!repairedRaw || repairedRaw.startsWith("\u26A0\uFE0F") || repairedRaw.length < 200) {
12204
12254
  onProgress?.("@repair", `\u26A0\uFE0F Repair round ${round} produced invalid output \u2014 stopping repair loop`);
12205
12255
  break;
@@ -12224,7 +12274,7 @@ ${currentContent}` }],
12224
12274
  title: lessonTitle,
12225
12275
  frameworkExcerpt: framework ? buildFrameworkExcerptForLesson(framework, lessonId) : void 0,
12226
12276
  styleGuideExcerpt: effectiveStyleGuide || void 0,
12227
- canonicalExposition: expositionContext ? expositionContext.slice(0, 3e3) : void 0
12277
+ canonicalExposition: expositionContext ? buildExpositionExcerpt(expositionContext, { budget: 3e3, sectionLanguageContract: slcMarkdown, artifactType: "KNOWLEDGE_EXPOSITION" }).excerpt : void 0
12228
12278
  }),
12229
12279
  standardStatements: Object.keys(standardStatements).length > 0 ? standardStatements : void 0
12230
12280
  });
@@ -12282,7 +12332,9 @@ ${currentContent}` }],
12282
12332
  pedagogy,
12283
12333
  producedArtifacts,
12284
12334
  pausedForReview: true,
12285
- gateStatus: "judge_needs_revision"
12335
+ gateStatus: "judge_needs_revision",
12336
+ contextInjection: expositionInjection || lessonInjection,
12337
+ promptChars: commonContext.length
12286
12338
  };
12287
12339
  }
12288
12340
  }
@@ -12305,7 +12357,9 @@ ${currentContent}` }],
12305
12357
  pedagogy,
12306
12358
  producedArtifacts,
12307
12359
  pausedForReview: true,
12308
- gateStatus: "judge_error"
12360
+ gateStatus: "judge_error",
12361
+ contextInjection: expositionInjection,
12362
+ promptChars: commonContext.length
12309
12363
  };
12310
12364
  }
12311
12365
  }
@@ -12335,7 +12389,9 @@ ${currentContent}` }],
12335
12389
  pedagogy,
12336
12390
  producedArtifacts,
12337
12391
  pausedForReview: true,
12338
- gateStatus: "awaiting_approval"
12392
+ gateStatus: "awaiting_approval",
12393
+ contextInjection: expositionInjection,
12394
+ promptChars: commonContext.length
12339
12395
  };
12340
12396
  }
12341
12397
  if (isLessonApproved && currentLessonInfo?.state !== "approved") {
@@ -12352,6 +12408,13 @@ ${currentContent}` }],
12352
12408
  artifactType: "LESSON"
12353
12409
  });
12354
12410
  const lessonExcerpt = lessonExcerptResult.excerpt;
12411
+ const lessonInjectionMeta = {
12412
+ source: "LESSON",
12413
+ verified: lessonExcerptResult.verified,
12414
+ sectionAware: lessonExcerptResult.sectionAware,
12415
+ issues: lessonExcerptResult.issues
12416
+ };
12417
+ lessonInjection = lessonInjectionMeta;
12355
12418
  const satelliteContext = `${commonContext}
12356
12419
 
12357
12420
  [CANONICAL LESSON PLAN (${pedagogyLabel})]:
@@ -12991,7 +13054,9 @@ ${buildHeadingDirective("EXT", slcMarkdown, targetLang)}`;
12991
13054
  lessonId: lessonCode,
12992
13055
  pedagogy,
12993
13056
  producedArtifacts,
12994
- pausedForReview: false
13057
+ pausedForReview: false,
13058
+ contextInjection: lessonInjection && lessonInjection.verified ? expositionInjection || lessonInjection : lessonInjection || expositionInjection,
13059
+ promptChars: commonContext.length
12995
13060
  };
12996
13061
  }
12997
13062
  function parseLessonObjectives(lessonContent, concept, bloomLevel) {