@thanh01.pmt/curriculum-kit 1.3.0 → 1.4.0

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
@@ -9842,6 +9842,7 @@ function buildSectionAwareExcerpt(markdown, opts = {}) {
9842
9842
  }
9843
9843
  const slcMap = normalizeSlcContract(opts.sectionLanguageContract);
9844
9844
  const sections = parseMarkdownSections(source, slcMap, opts.artifactType);
9845
+ const metaBlock = buildMetaBlock(source);
9845
9846
  if (sections.length === 0) {
9846
9847
  issues.push("parse:no-sections");
9847
9848
  const excerpt2 = truncateByBudget(source, budget);
@@ -9875,12 +9876,13 @@ function buildSectionAwareExcerpt(markdown, opts = {}) {
9875
9876
  const block = `## ${heading}
9876
9877
 
9877
9878
  ${trimmedBody}`;
9878
- if (used + block.length > budget && blocks.length > 0) return false;
9879
+ if (used + block.length > budget && used > 0) return false;
9879
9880
  blocks.push(block);
9880
9881
  used += block.length;
9881
9882
  includedSections.push(key);
9882
9883
  return true;
9883
9884
  };
9885
+ blocks.push(metaBlock);
9884
9886
  let sectionAware = false;
9885
9887
  for (const key of priorities) {
9886
9888
  const sec = byCanonical.get(key);
@@ -9897,6 +9899,9 @@ ${trimmedBody}`;
9897
9899
  if (used >= budget) break;
9898
9900
  if (!tryAdd(sec.cleanTitle, sec.body, sec.cleanTitle)) break;
9899
9901
  }
9902
+ if (includedSections.length === 0 && unmatched.length > 0) {
9903
+ issues.push("parse:no-canonical-resolution");
9904
+ }
9900
9905
  if (priorities.length > 0 && !priorities.some((k) => includedSections.includes(k))) {
9901
9906
  issues.push("parse:no-priority-resolution");
9902
9907
  }
@@ -9905,14 +9910,14 @@ ${trimmedBody}`;
9905
9910
  excerpt = truncateByBudget(excerpt, budget);
9906
9911
  }
9907
9912
  const effectiveMinChars = Math.min(minContentChars, source.length);
9908
- if (excerpt.length < effectiveMinChars) {
9913
+ if (excerpt.length - metaBlock.length < effectiveMinChars) {
9909
9914
  issues.push("content:insufficient");
9910
9915
  }
9911
- const verified = !issues.includes("parse:no-priority-resolution") && excerpt.length >= effectiveMinChars;
9916
+ const verified = !issues.includes("parse:no-priority-resolution") && !issues.includes("parse:no-canonical-resolution") && excerpt.length - metaBlock.length >= effectiveMinChars;
9912
9917
  return {
9913
9918
  excerpt,
9914
9919
  verified,
9915
- sectionAware: sectionAware || priorities.length === 0 && sections.length > 0,
9920
+ sectionAware: sectionAware || unmatched.length === 0,
9916
9921
  includedSections,
9917
9922
  issues,
9918
9923
  tokenEstimate: Math.round(excerpt.length / 4)
@@ -10028,6 +10033,18 @@ function truncateByBudget(source, budget) {
10028
10033
  if (fenceCount % 2 === 1) cut += "\n```";
10029
10034
  return cut;
10030
10035
  }
10036
+ function buildMetaBlock(source) {
10037
+ const fm = source.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/);
10038
+ const pick = (key) => {
10039
+ if (!fm) return "";
10040
+ const m = fm[1].match(new RegExp(`^${key}:\\s*"?([^"\\n]+)"?`, "m"));
10041
+ return (m?.[1] || "").trim();
10042
+ };
10043
+ const id = pick("id");
10044
+ const title = pick("title");
10045
+ const type = pick("type");
10046
+ return `<!-- SOURCE: ${type || "ARTIFACT"} | ${id || "unknown"}${title ? ` \u2014 ${title}` : ""} (section-aware excerpt; canonical knowledge, do not contradict) -->`;
10047
+ }
10031
10048
 
10032
10049
  // src/services/knowledgeExpositionService.ts
10033
10050
  var EXPOSITION_REL = (lessonCode) => "_sot/expositions/" + lessonCode + ".md";
@@ -10176,7 +10193,7 @@ async function ensureExpositionForLesson(storage, projectId, lessonCode, options
10176
10193
  storage,
10177
10194
  gates: options.gates
10178
10195
  });
10179
- return { relPath: result.relPath, context: buildExpositionContext(result.content), reused: result.reused };
10196
+ return { relPath: result.relPath, context: buildExpositionContext(result.content), reused: result.reused, rawContent: result.content };
10180
10197
  }
10181
10198
  init_errors();
10182
10199
  var asArr = (v) => Array.isArray(v) ? v : [];
@@ -11887,6 +11904,8 @@ async function produceSingleLesson(storage, projectId, lessonId, options = {}) {
11887
11904
  targetLang = targetLang || "vi";
11888
11905
  const languageDirective = buildLanguageDirective(targetLang);
11889
11906
  let expositionContext = "";
11907
+ let expositionInjection;
11908
+ let lessonInjection;
11890
11909
  let sessionSliceContext = "";
11891
11910
  try {
11892
11911
  const exposition = await ensureExpositionForLesson(storage, projectId, lessonCode, {
@@ -11894,7 +11913,18 @@ async function produceSingleLesson(storage, projectId, lessonId, options = {}) {
11894
11913
  force: options.force
11895
11914
  });
11896
11915
  if (exposition) {
11897
- expositionContext = exposition.context;
11916
+ const expoExcerpt = buildExpositionExcerpt(exposition.rawContent || exposition.context, {
11917
+ budget: 6e3,
11918
+ sectionLanguageContract: slcMarkdown,
11919
+ artifactType: "KNOWLEDGE_EXPOSITION"
11920
+ });
11921
+ expositionContext = expoExcerpt.excerpt || exposition.context;
11922
+ expositionInjection = {
11923
+ source: "KNOWLEDGE_EXPOSITION",
11924
+ verified: expoExcerpt.verified,
11925
+ sectionAware: expoExcerpt.sectionAware,
11926
+ issues: expoExcerpt.issues
11927
+ };
11898
11928
  onProgress?.("@content", `[EXPOSITION] ${exposition.reused ? "Reused" : "Generated"} \`\${exposition.relPath}\` (plan-driven knowledge source)...`);
11899
11929
  } else {
11900
11930
  onProgress?.("@content", "[EXPOSITION] No CURRICULUM_PLAN.json \u2014 running legacy knowledge flow (no plan-driven exposition).");
@@ -12134,6 +12164,7 @@ ${yamlBlock.trim()}
12134
12164
  };
12135
12165
  var injectYamlReviewMetadata = injectYamlReviewMetadata2;
12136
12166
  const judgeObjectives = parseLessonObjectives(lessonContent, concept, bloomLevel);
12167
+ const judgeExpositionExcerpt = expositionContext ? buildExpositionExcerpt(expositionContext, { budget: 3e3, sectionLanguageContract: slcMarkdown, artifactType: "KNOWLEDGE_EXPOSITION" }).excerpt : void 0;
12137
12168
  const judgeResult = await LLMJudgeEngine.evaluateArtifactWithLLM({
12138
12169
  targetArtifactType: "LESSON",
12139
12170
  lessonId: lessonCode,
@@ -12145,7 +12176,7 @@ ${yamlBlock.trim()}
12145
12176
  title: lessonTitle,
12146
12177
  frameworkExcerpt: framework ? buildFrameworkExcerptForLesson(framework, lessonId) : void 0,
12147
12178
  styleGuideExcerpt: effectiveStyleGuide || void 0,
12148
- canonicalExposition: expositionContext ? expositionContext.slice(0, 3e3) : void 0
12179
+ canonicalExposition: judgeExpositionExcerpt
12149
12180
  }),
12150
12181
  standardStatements: Object.keys(standardStatements).length > 0 ? standardStatements : void 0
12151
12182
  });
@@ -12200,6 +12231,16 @@ ${currentContent}` }],
12200
12231
  options.onProgress?.("@repair", chunk, { type: type || "content", artifactType: "LESSON" });
12201
12232
  }
12202
12233
  );
12234
+ const repairedExcerptCheck = buildSectionAwareExcerpt(repairedRaw, {
12235
+ priorities: DEFAULT_LESSON_PRIORITIES,
12236
+ budget: 12e3,
12237
+ sectionLanguageContract: slcMarkdown,
12238
+ artifactType: "LESSON"
12239
+ });
12240
+ if (repairedExcerptCheck.issues.includes("parse:no-priority-resolution")) {
12241
+ onProgress?.("@repair", `\u26A0\uFE0F Repair round ${round} lost mandatory section structure (no priority section resolved) \u2014 rejecting repaired draft, retaining previous`);
12242
+ break;
12243
+ }
12203
12244
  if (!repairedRaw || repairedRaw.startsWith("\u26A0\uFE0F") || repairedRaw.length < 200) {
12204
12245
  onProgress?.("@repair", `\u26A0\uFE0F Repair round ${round} produced invalid output \u2014 stopping repair loop`);
12205
12246
  break;
@@ -12224,7 +12265,7 @@ ${currentContent}` }],
12224
12265
  title: lessonTitle,
12225
12266
  frameworkExcerpt: framework ? buildFrameworkExcerptForLesson(framework, lessonId) : void 0,
12226
12267
  styleGuideExcerpt: effectiveStyleGuide || void 0,
12227
- canonicalExposition: expositionContext ? expositionContext.slice(0, 3e3) : void 0
12268
+ canonicalExposition: expositionContext ? buildExpositionExcerpt(expositionContext, { budget: 3e3, sectionLanguageContract: slcMarkdown, artifactType: "KNOWLEDGE_EXPOSITION" }).excerpt : void 0
12228
12269
  }),
12229
12270
  standardStatements: Object.keys(standardStatements).length > 0 ? standardStatements : void 0
12230
12271
  });
@@ -12282,7 +12323,9 @@ ${currentContent}` }],
12282
12323
  pedagogy,
12283
12324
  producedArtifacts,
12284
12325
  pausedForReview: true,
12285
- gateStatus: "judge_needs_revision"
12326
+ gateStatus: "judge_needs_revision",
12327
+ contextInjection: expositionInjection || lessonInjection,
12328
+ promptChars: commonContext.length
12286
12329
  };
12287
12330
  }
12288
12331
  }
@@ -12305,7 +12348,9 @@ ${currentContent}` }],
12305
12348
  pedagogy,
12306
12349
  producedArtifacts,
12307
12350
  pausedForReview: true,
12308
- gateStatus: "judge_error"
12351
+ gateStatus: "judge_error",
12352
+ contextInjection: expositionInjection,
12353
+ promptChars: commonContext.length
12309
12354
  };
12310
12355
  }
12311
12356
  }
@@ -12335,7 +12380,9 @@ ${currentContent}` }],
12335
12380
  pedagogy,
12336
12381
  producedArtifacts,
12337
12382
  pausedForReview: true,
12338
- gateStatus: "awaiting_approval"
12383
+ gateStatus: "awaiting_approval",
12384
+ contextInjection: expositionInjection,
12385
+ promptChars: commonContext.length
12339
12386
  };
12340
12387
  }
12341
12388
  if (isLessonApproved && currentLessonInfo?.state !== "approved") {
@@ -12352,6 +12399,13 @@ ${currentContent}` }],
12352
12399
  artifactType: "LESSON"
12353
12400
  });
12354
12401
  const lessonExcerpt = lessonExcerptResult.excerpt;
12402
+ const lessonInjectionMeta = {
12403
+ source: "LESSON",
12404
+ verified: lessonExcerptResult.verified,
12405
+ sectionAware: lessonExcerptResult.sectionAware,
12406
+ issues: lessonExcerptResult.issues
12407
+ };
12408
+ lessonInjection = lessonInjectionMeta;
12355
12409
  const satelliteContext = `${commonContext}
12356
12410
 
12357
12411
  [CANONICAL LESSON PLAN (${pedagogyLabel})]:
@@ -12991,7 +13045,9 @@ ${buildHeadingDirective("EXT", slcMarkdown, targetLang)}`;
12991
13045
  lessonId: lessonCode,
12992
13046
  pedagogy,
12993
13047
  producedArtifacts,
12994
- pausedForReview: false
13048
+ pausedForReview: false,
13049
+ contextInjection: lessonInjection && lessonInjection.verified ? expositionInjection || lessonInjection : lessonInjection || expositionInjection,
13050
+ promptChars: commonContext.length
12995
13051
  };
12996
13052
  }
12997
13053
  function parseLessonObjectives(lessonContent, concept, bloomLevel) {