@thanh01.pmt/curriculum-kit 1.4.48 → 1.4.50

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
@@ -4875,9 +4875,9 @@ Evaluate the candidate artifact across these 6 dimensions:
4875
4875
  - 0 pts: Missing critical sections.
4876
4876
 
4877
4877
  5. **Technical & Conceptual Accuracy (15 pts):**
4878
- - 15 pts: Code snippets, logic flows, architectural descriptions, and Mermaid diagrams are conceptually sound and valid. If \`canonicalExposition\` or \`[KNOWLEDGE_EXPOSITION]\` is provided, content strictly adheres to canonical concepts without contradicting definitions or examples.
4878
+ - 15 pts: Code snippets, logic flows, architectural descriptions, and Mermaid diagrams are conceptually sound and valid. Every code identifier uses the REAL standard-library/API spelling of the declared stack (grounded in REFERENCE_PACK / canonical LESSON) \u2014 internal consistency with a glossary term name is NOT evidence of correctness. If \`canonicalExposition\` or \`[KNOWLEDGE_EXPOSITION]\` is provided, content strictly adheres to canonical concepts without contradicting definitions or examples.
4879
4879
  - 8 pts: Minor syntax inaccuracies or unidiomatic code that does not break core concepts.
4880
- - 0 pts: Severe technical hallucinations, broken logic, invalid diagrams, or direct contradiction of canonical exposition knowledge.
4880
+ - 0 pts: Severe technical hallucinations, broken logic, invalid diagrams, invented casing/spelling rules for standard-library identifiers, or direct contradiction of canonical exposition knowledge.
4881
4881
 
4882
4882
  6. **Contract Consistency & Zero-Drift (10 pts):**
4883
4883
  - 10 pts: Satellite artifact strictly adheres to the scope, tech stack, and objectives of Lesson ${lessonId} without introducing unrelated topics. If \`canonicalLessonExcerpt\` is present in candidate JSON, all satellite phases/activities strictly operationalize the master Lesson Flow and Activity Sequence without inventing divergent timelines.
@@ -10208,6 +10208,38 @@ function checkScopeDrift(content, scopedKeywords, tolerance = 0) {
10208
10208
  evidence: drift
10209
10209
  };
10210
10210
  }
10211
+ function checkIdentifierCasing(content, refPack) {
10212
+ if (!refPack || !refPack.trim()) return null;
10213
+ const canonical = /* @__PURE__ */ new Map();
10214
+ const remember = (id) => {
10215
+ if (id.length < 2) return;
10216
+ const key = id.toLowerCase().replace(/_/g, "");
10217
+ if (!canonical.has(key)) canonical.set(key, id);
10218
+ };
10219
+ for (const m of refPack.matchAll(/`([A-Za-z_][A-Za-z0-9_]*)\s*\(/g)) remember(m[1]);
10220
+ for (const m of refPack.matchAll(/\b([A-Za-z_][A-Za-z0-9_]*)\s*\(/g)) remember(m[1]);
10221
+ for (const m of refPack.matchAll(/`([A-Za-z_][A-Za-z0-9_]*)`/g)) remember(m[1]);
10222
+ if (canonical.size === 0) return null;
10223
+ const RESERVED = /* @__PURE__ */ new Set(["if", "for", "while", "switch", "func", "return", "guard", "catch", "else", "in"]);
10224
+ const violations = [];
10225
+ const fenceRe = /```[a-zA-Z]*\n([\s\S]*?)```/g;
10226
+ for (const fence of content.matchAll(fenceRe)) {
10227
+ for (const m of fence[1].matchAll(/\b([A-Za-z_][A-Za-z0-9_]*)\s*\(/g)) {
10228
+ const tok = m[1];
10229
+ if (RESERVED.has(tok.toLowerCase())) continue;
10230
+ const real = canonical.get(tok.toLowerCase().replace(/_/g, ""));
10231
+ if (real && real !== tok && !violations.includes(`${tok} \u2192 ${real}`)) {
10232
+ violations.push(`${tok} \u2192 ${real}`);
10233
+ }
10234
+ }
10235
+ }
10236
+ if (violations.length === 0) return null;
10237
+ return {
10238
+ code: "identifier-casing",
10239
+ detail: `Code d\xF9ng identifier l\u1EC7ch casing so v\u1EDBi REFERENCE_PACK (ngu\u1ED3n s\u1EF1 th\u1EADt k\u1EF9 thu\u1EADt): ${violations.join(", ")}. Identifier trong code PH\u1EA2I copy nguy\xEAn d\u1EA1ng t\u1EEB REFERENCE_PACK \u2014 KH\xD4NG suy ra casing t\u1EEB t\xEAn keyword/concept (ch\xFAng l\xE0 nh\xE3n s\u01B0 ph\u1EA1m, kh\xF4ng ph\u1EA3i API).`,
10240
+ evidence: violations.slice(0, 5)
10241
+ };
10242
+ }
10211
10243
  function validateArtifactDeterministic(input) {
10212
10244
  const issues = [];
10213
10245
  const cjk = scanCjkLeaks(input.content);
@@ -10215,6 +10247,8 @@ function validateArtifactDeterministic(input) {
10215
10247
  if (input.refPack) {
10216
10248
  const ver = checkVersionGroundTruth(input.content, input.refPack);
10217
10249
  if (ver) issues.push(ver);
10250
+ const casing = checkIdentifierCasing(input.content, input.refPack);
10251
+ if (casing) issues.push(casing);
10218
10252
  }
10219
10253
  if (input.scopedKeywords && input.scopedKeywords.length > 0) {
10220
10254
  const scope = checkScopeDrift(input.content, input.scopedKeywords);
@@ -10326,9 +10360,10 @@ function buildSystemPrompt(targetLanguage = "vi", techStack, hardwarePlatform) {
10326
10360
  "Rules:",
10327
10361
  "1. Cover EXACTLY the scoped keywords and concepts \u2014 nothing beyond the session scope.",
10328
10362
  "2. Every Key Term definition must be consistent with the glossary entry provided.",
10329
- "3. Respect each concept depth target (ULO/CIO/SIO) \u2014 do not overshoot.",
10330
- "4. Student-facing only: no teacher instructions, no classroom management text.",
10331
- "5. Output the full markdown document following the provided section skeleton exactly (headers and placeholders preserved).",
10363
+ '3. IDENTIFIER GROUND TRUTH: glossary/keyword names are pedagogical concept labels \u2014 NEVER derive code identifier casing or spelling from them. In code samples, copy identifiers EXACTLY from the REFERENCE_PACK ground truth (e.g. the standard library function is `print` lowercase; "Print" as a topic label is not an API symbol).',
10364
+ "4. Respect each concept depth target (ULO/CIO/SIO) \u2014 do not overshoot.",
10365
+ "5. Student-facing only: no teacher instructions, no classroom management text.",
10366
+ "6. Output the full markdown document following the provided section skeleton exactly (headers and placeholders preserved).",
10332
10367
  domainGuardrails,
10333
10368
  langDirective
10334
10369
  ].join("\n");
@@ -10995,10 +11030,13 @@ function buildUnitGroupings(graph, orderedIds) {
10995
11030
  }
10996
11031
  function computePackingSpec(constraints) {
10997
11032
  const overheadMinutes = Math.round(constraints.session_duration_minutes * constraints.overhead_ratio);
11033
+ const upperAge = constraints.age_band?.[1];
11034
+ const maxNew = upperAge !== void 0 && upperAge > 15 ? MAX_NEW_CONCEPTS_PER_SESSION_ADULT : MAX_NEW_CONCEPTS_PER_SESSION_K12;
10998
11035
  return {
10999
11036
  contentBudget: constraints.session_duration_minutes - overheadMinutes,
11000
11037
  overheadMinutes,
11001
- sessionDurationMinutes: constraints.session_duration_minutes
11038
+ sessionDurationMinutes: constraints.session_duration_minutes,
11039
+ maxNewConceptsPerSession: maxNew
11002
11040
  };
11003
11041
  }
11004
11042
  var filledOf = (s) => s.knowledgeMinutes + s.practiceMinutes;
@@ -11081,6 +11119,15 @@ function packUnitSessions(group, nodeById, spec, warnings) {
11081
11119
  continue;
11082
11120
  }
11083
11121
  const minutes = node.estimated_minutes;
11122
+ if (spec.maxNewConceptsPerSession && spec.maxNewConceptsPerSession > 0 && current.entries.length > 0) {
11123
+ const currentConcepts = new Set(current.nodeIds.flatMap((nid) => nodeById.get(nid)?.concept_codes || []));
11124
+ const incoming = nodeById.get(id)?.concept_codes || [];
11125
+ const wouldAdd = incoming.filter((c) => !currentConcepts.has(c)).length;
11126
+ if (currentConcepts.size + wouldAdd > spec.maxNewConceptsPerSession) {
11127
+ warnings.push(`ZPD pack flush: session ${group.key}#${sessions.length + 1} cut at ${currentConcepts.size} new concepts (budget ${spec.maxNewConceptsPerSession})`);
11128
+ flush();
11129
+ }
11130
+ }
11084
11131
  if (current.entries.length > 0 && filled() + minutes > spec.contentBudget) {
11085
11132
  const displaced = cutBackToCheckpoint(current, nodeById, spec, warnings);
11086
11133
  if (displaced.length > 0) {
@@ -11239,12 +11286,12 @@ function checkSessionZpd(sessionIndex, nodeIds, nodeById, allSeenConcepts, allSe
11239
11286
  const knownKeywords = allKeywords.filter((k) => allSeenKeywords.has(k.toLowerCase()) || entryKw.has(k.toLowerCase()));
11240
11287
  const issues = [];
11241
11288
  let verdict = "OK";
11242
- if (concepts.length > 0 && newConcepts.length > 2) {
11243
- verdict = "TOO_MANY_NEW";
11244
- issues.push(`${newConcepts.length} new concepts exceed ZPD limit (max 2)`);
11245
- } else if (concepts.length === 0 && newKeywords.length > 4) {
11289
+ if (newConcepts.length > MAX_NEW_CONCEPTS_PER_SESSION_K12) {
11246
11290
  verdict = "TOO_MANY_NEW";
11247
- issues.push(`${newKeywords.length} new keywords exceed ZPD limit (max 4)`);
11291
+ issues.push(`${newConcepts.length} new concepts exceed ZPD limit (max ${MAX_NEW_CONCEPTS_PER_SESSION_K12})`);
11292
+ }
11293
+ if (newKeywords.length > 4) {
11294
+ issues.push(`Evidence for judge: ${newKeywords.length} new keywords in session (noisy proxy, not a gate)`);
11248
11295
  }
11249
11296
  if (sessionIndex > 0) {
11250
11297
  if (concepts.length > 1 && knownConcepts.length === 0) {
@@ -27356,6 +27403,7 @@ Your task is to author a canonical, publication-grade learning artifact of type
27356
27403
  ${langDirective}
27357
27404
  ${headingDirective}
27358
27405
  2. NON-MOCK CODE INVARIANT: Any code snippet provided MUST be 100% syntactically valid, idiomatic, and executable without placeholders or fake functions.
27406
+ 2b. IDENTIFIER GROUND TRUTH: Never derive identifier casing/spelling from glossary term or concept names (they are pedagogical labels). Copy identifiers EXACTLY from the canonical LESSON and REFERENCE_PACK ground truth present in the context.
27359
27407
  3. PEDAGOGICAL GROUNDING: Strictly align with the ${pedagogy.toUpperCase()} model, Bloom's Revised Taxonomy, and IEEE/ACM CS2023 guidelines.
27360
27408
  4. SCAFFOLDING LEVEL: "${scaffoldingLevel.toUpperCase()}".${scaffoldingRule}
27361
27409
  5. TARGET AUDIENCE: ${gradeLevel ? `Grade ${gradeLevel}, ` : ""}${targetAge} on ${hwStr}.${deviceRule}${classDynamicsRule}${extraContextRule}
@@ -29915,14 +29963,14 @@ var DeterministicStructuralLinter = class {
29915
29963
  let score = 100;
29916
29964
  for (const s of plan.sessions) {
29917
29965
  if (s.zpd_status?.verdict === "TOO_MANY_NEW") {
29918
- score -= 10;
29966
+ score -= 25;
29919
29967
  findings.push({
29920
29968
  id: `PLAN_TOO_MANY_NEW_${s.id}`,
29921
29969
  dimension: "CONSTRUCTIVE_ALIGNMENT",
29922
- severity: "MINOR",
29970
+ severity: "CRITICAL",
29923
29971
  title: `Cognitive Overload in Session ${s.id}`,
29924
- description: s.zpd_status.issues.join("; ") || `Session introduces too many new concepts/keywords, exceeding ZPD capacity.`,
29925
- remediationAdvice: `Distribute new concepts across multiple sessions or introduce via scaffolding.`,
29972
+ description: s.zpd_status.issues.join("; ") || `Session introduces more new concepts than the ZPD limit allows.`,
29973
+ remediationAdvice: `Split the session: move overflow concepts to a later session (the packer flushes on concept budget automatically).`,
29926
29974
  affectedElement: s.id
29927
29975
  });
29928
29976
  } else if (s.zpd_status?.verdict === "NO_ZPD_BRIDGE") {