@thanh01.pmt/curriculum-kit 1.4.49 → 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.d.cts CHANGED
@@ -1123,6 +1123,13 @@ interface PackingSpec {
1123
1123
  contentBudget: number;
1124
1124
  overheadMinutes: number;
1125
1125
  sessionDurationMinutes: number;
1126
+ /** 2026-09-18 RC-2: cognitive budget — max DISTINCT new concepts per
1127
+ * session (Vygotsky ZPD constant). 0 disables the check. The minute
1128
+ * budget alone packs knowledge-dense sessions (observed: 4 concepts in
1129
+ * one 90m K-12 session); flushing early on this budget is a STRUCTURAL
1130
+ * rule — it counts and cuts, it never reorders across prerequisite
1131
+ * edges (teaching order preserved by construction). */
1132
+ maxNewConceptsPerSession?: number;
1126
1133
  }
1127
1134
  declare function computePackingSpec(constraints: PlanConstraints): PackingSpec;
1128
1135
  interface PackedEntry {
package/dist/index.d.ts CHANGED
@@ -1123,6 +1123,13 @@ interface PackingSpec {
1123
1123
  contentBudget: number;
1124
1124
  overheadMinutes: number;
1125
1125
  sessionDurationMinutes: number;
1126
+ /** 2026-09-18 RC-2: cognitive budget — max DISTINCT new concepts per
1127
+ * session (Vygotsky ZPD constant). 0 disables the check. The minute
1128
+ * budget alone packs knowledge-dense sessions (observed: 4 concepts in
1129
+ * one 90m K-12 session); flushing early on this budget is a STRUCTURAL
1130
+ * rule — it counts and cuts, it never reorders across prerequisite
1131
+ * edges (teaching order preserved by construction). */
1132
+ maxNewConceptsPerSession?: number;
1126
1133
  }
1127
1134
  declare function computePackingSpec(constraints: PlanConstraints): PackingSpec;
1128
1135
  interface PackedEntry {
package/dist/index.mjs CHANGED
@@ -11018,10 +11018,13 @@ function buildUnitGroupings(graph, orderedIds) {
11018
11018
  }
11019
11019
  function computePackingSpec(constraints) {
11020
11020
  const overheadMinutes = Math.round(constraints.session_duration_minutes * constraints.overhead_ratio);
11021
+ const upperAge = constraints.age_band?.[1];
11022
+ const maxNew = upperAge !== void 0 && upperAge > 15 ? MAX_NEW_CONCEPTS_PER_SESSION_ADULT : MAX_NEW_CONCEPTS_PER_SESSION_K12;
11021
11023
  return {
11022
11024
  contentBudget: constraints.session_duration_minutes - overheadMinutes,
11023
11025
  overheadMinutes,
11024
- sessionDurationMinutes: constraints.session_duration_minutes
11026
+ sessionDurationMinutes: constraints.session_duration_minutes,
11027
+ maxNewConceptsPerSession: maxNew
11025
11028
  };
11026
11029
  }
11027
11030
  var filledOf = (s) => s.knowledgeMinutes + s.practiceMinutes;
@@ -11104,6 +11107,15 @@ function packUnitSessions(group, nodeById, spec, warnings) {
11104
11107
  continue;
11105
11108
  }
11106
11109
  const minutes = node.estimated_minutes;
11110
+ if (spec.maxNewConceptsPerSession && spec.maxNewConceptsPerSession > 0 && current.entries.length > 0) {
11111
+ const currentConcepts = new Set(current.nodeIds.flatMap((nid) => nodeById.get(nid)?.concept_codes || []));
11112
+ const incoming = nodeById.get(id)?.concept_codes || [];
11113
+ const wouldAdd = incoming.filter((c) => !currentConcepts.has(c)).length;
11114
+ if (currentConcepts.size + wouldAdd > spec.maxNewConceptsPerSession) {
11115
+ warnings.push(`ZPD pack flush: session ${group.key}#${sessions.length + 1} cut at ${currentConcepts.size} new concepts (budget ${spec.maxNewConceptsPerSession})`);
11116
+ flush();
11117
+ }
11118
+ }
11107
11119
  if (current.entries.length > 0 && filled() + minutes > spec.contentBudget) {
11108
11120
  const displaced = cutBackToCheckpoint(current, nodeById, spec, warnings);
11109
11121
  if (displaced.length > 0) {
@@ -11262,12 +11274,12 @@ function checkSessionZpd(sessionIndex, nodeIds, nodeById, allSeenConcepts, allSe
11262
11274
  const knownKeywords = allKeywords.filter((k) => allSeenKeywords.has(k.toLowerCase()) || entryKw.has(k.toLowerCase()));
11263
11275
  const issues = [];
11264
11276
  let verdict = "OK";
11265
- if (concepts.length > 0 && newConcepts.length > 2) {
11277
+ if (newConcepts.length > MAX_NEW_CONCEPTS_PER_SESSION_K12) {
11266
11278
  verdict = "TOO_MANY_NEW";
11267
- issues.push(`${newConcepts.length} new concepts exceed ZPD limit (max 2)`);
11268
- } else if (concepts.length === 0 && newKeywords.length > 4) {
11269
- verdict = "TOO_MANY_NEW";
11270
- issues.push(`${newKeywords.length} new keywords exceed ZPD limit (max 4)`);
11279
+ issues.push(`${newConcepts.length} new concepts exceed ZPD limit (max ${MAX_NEW_CONCEPTS_PER_SESSION_K12})`);
11280
+ }
11281
+ if (newKeywords.length > 4) {
11282
+ issues.push(`Evidence for judge: ${newKeywords.length} new keywords in session (noisy proxy, not a gate)`);
11271
11283
  }
11272
11284
  if (sessionIndex > 0) {
11273
11285
  if (concepts.length > 1 && knownConcepts.length === 0) {
@@ -29939,14 +29951,14 @@ var DeterministicStructuralLinter = class {
29939
29951
  let score = 100;
29940
29952
  for (const s of plan.sessions) {
29941
29953
  if (s.zpd_status?.verdict === "TOO_MANY_NEW") {
29942
- score -= 10;
29954
+ score -= 25;
29943
29955
  findings.push({
29944
29956
  id: `PLAN_TOO_MANY_NEW_${s.id}`,
29945
29957
  dimension: "CONSTRUCTIVE_ALIGNMENT",
29946
- severity: "MINOR",
29958
+ severity: "CRITICAL",
29947
29959
  title: `Cognitive Overload in Session ${s.id}`,
29948
- description: s.zpd_status.issues.join("; ") || `Session introduces too many new concepts/keywords, exceeding ZPD capacity.`,
29949
- remediationAdvice: `Distribute new concepts across multiple sessions or introduce via scaffolding.`,
29960
+ description: s.zpd_status.issues.join("; ") || `Session introduces more new concepts than the ZPD limit allows.`,
29961
+ remediationAdvice: `Split the session: move overflow concepts to a later session (the packer flushes on concept budget automatically).`,
29950
29962
  affectedElement: s.id
29951
29963
  });
29952
29964
  } else if (s.zpd_status?.verdict === "NO_ZPD_BRIDGE") {