@thanh01.pmt/curriculum-kit 1.4.52 → 1.4.53
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 +60 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.mjs +60 -16
- package/dist/index.mjs.map +1 -1
- package/dist/workflow/index.cjs.map +1 -1
- package/dist/workflow/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -11805,10 +11805,16 @@ function canCoalesceWithinZpdBudget(curr, next, nodeById, maxNew) {
|
|
|
11805
11805
|
}
|
|
11806
11806
|
function packUnitSessions(group, nodeById, spec, warnings) {
|
|
11807
11807
|
const sessions = [];
|
|
11808
|
-
const fresh = () => ({ groupId: group.key, nodeIds: [], entries: [], knowledgeMinutes: 0, practiceMinutes: 0, oversized: false });
|
|
11808
|
+
const fresh = () => ({ groupId: group.key, nodeIds: [], entries: [], knowledgeMinutes: 0, practiceMinutes: 0, oversized: false, cognitiveNew: 0 });
|
|
11809
11809
|
let current = fresh();
|
|
11810
|
+
const seenConcepts = /* @__PURE__ */ new Set();
|
|
11810
11811
|
const flush = () => {
|
|
11811
|
-
if (current.entries.length > 0)
|
|
11812
|
+
if (current.entries.length > 0) {
|
|
11813
|
+
for (const nid of current.nodeIds) {
|
|
11814
|
+
for (const c of nodeById.get(nid)?.concept_codes || []) seenConcepts.add(c);
|
|
11815
|
+
}
|
|
11816
|
+
sessions.push(current);
|
|
11817
|
+
}
|
|
11812
11818
|
current = fresh();
|
|
11813
11819
|
};
|
|
11814
11820
|
const filled = () => current.knowledgeMinutes + current.practiceMinutes;
|
|
@@ -11850,14 +11856,11 @@ function packUnitSessions(group, nodeById, spec, warnings) {
|
|
|
11850
11856
|
continue;
|
|
11851
11857
|
}
|
|
11852
11858
|
const minutes = node.estimated_minutes;
|
|
11853
|
-
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
warnings.push(`ZPD pack flush: session ${group.key}#${sessions.length + 1} cut at ${currentConcepts.size} new concepts (budget ${spec.maxNewConceptsPerSession})`);
|
|
11859
|
-
flush();
|
|
11860
|
-
}
|
|
11859
|
+
const localConcepts = new Set(current.nodeIds.flatMap((nid) => nodeById.get(nid)?.concept_codes || []));
|
|
11860
|
+
const nodeNew = node.concept_codes.filter((c) => !seenConcepts.has(c) && !localConcepts.has(c)).length;
|
|
11861
|
+
if (spec.maxNewConceptsPerSession && spec.maxNewConceptsPerSession > 0 && current.entries.length > 0 && current.cognitiveNew + nodeNew > spec.maxNewConceptsPerSession) {
|
|
11862
|
+
warnings.push(`ZPD pack flush: session ${group.key}#${sessions.length + 1} cut at ${current.cognitiveNew} new concepts (budget ${spec.maxNewConceptsPerSession})`);
|
|
11863
|
+
flush();
|
|
11861
11864
|
}
|
|
11862
11865
|
if (current.entries.length > 0 && filled() + minutes > spec.contentBudget) {
|
|
11863
11866
|
const displaced = cutBackToCheckpoint(current, nodeById, spec, warnings);
|
|
@@ -11869,11 +11872,33 @@ function packUnitSessions(group, nodeById, spec, warnings) {
|
|
|
11869
11872
|
}
|
|
11870
11873
|
flush();
|
|
11871
11874
|
}
|
|
11872
|
-
if (isKnowledge)
|
|
11873
|
-
|
|
11875
|
+
if (isKnowledge) {
|
|
11876
|
+
current.knowledgeMinutes += minutes;
|
|
11877
|
+
current.cognitiveNew += nodeNew;
|
|
11878
|
+
} else {
|
|
11879
|
+
current.practiceMinutes += minutes;
|
|
11880
|
+
current.cognitiveNew += nodeNew;
|
|
11881
|
+
}
|
|
11874
11882
|
current.entries.push({ id, minutes, part: 1, totalParts: 1 });
|
|
11875
11883
|
current.nodeIds.push(id);
|
|
11876
|
-
if (filled() >= spec.contentBudget && current.entries.length >= MAX_SESSION_NODES)
|
|
11884
|
+
if (filled() >= spec.contentBudget && current.entries.length >= MAX_SESSION_NODES) {
|
|
11885
|
+
flush();
|
|
11886
|
+
} else if (!isKnowledge && current.entries.length < MAX_SESSION_NODES) {
|
|
11887
|
+
while (ids.length > 0 && current.entries.length < MAX_SESSION_NODES) {
|
|
11888
|
+
const nxtId = ids[0];
|
|
11889
|
+
const nxt = nodeById.get(nxtId);
|
|
11890
|
+
if (!nxt || nxt.kind !== "concept" || nxt.estimated_minutes > spec.contentBudget) break;
|
|
11891
|
+
const nxtLocal = new Set(current.nodeIds.flatMap((nid) => nodeById.get(nid)?.concept_codes || []));
|
|
11892
|
+
const nxtNew = nxt.concept_codes.filter((c) => !seenConcepts.has(c) && !nxtLocal.has(c)).length;
|
|
11893
|
+
if (spec.maxNewConceptsPerSession && spec.maxNewConceptsPerSession > 0 && current.cognitiveNew + nxtNew > spec.maxNewConceptsPerSession) break;
|
|
11894
|
+
if (filled() + nxt.estimated_minutes > spec.contentBudget) break;
|
|
11895
|
+
ids.shift();
|
|
11896
|
+
current.knowledgeMinutes += nxt.estimated_minutes;
|
|
11897
|
+
current.cognitiveNew += nxtNew;
|
|
11898
|
+
current.entries.push({ id: nxtId, minutes: nxt.estimated_minutes, part: 1, totalParts: 1 });
|
|
11899
|
+
current.nodeIds.push(nxtId);
|
|
11900
|
+
}
|
|
11901
|
+
}
|
|
11877
11902
|
}
|
|
11878
11903
|
flush();
|
|
11879
11904
|
return sessions;
|
|
@@ -12008,7 +12033,7 @@ function computeConceptSpiralProgression(sessions, nodeById) {
|
|
|
12008
12033
|
}
|
|
12009
12034
|
return encounters;
|
|
12010
12035
|
}
|
|
12011
|
-
function checkSessionZpd(sessionIndex, nodeIds, nodeById, allSeenConcepts, allSeenKeywords, entryKw, warnings) {
|
|
12036
|
+
function checkSessionZpd(sessionIndex, nodeIds, nodeById, allSeenConcepts, allSeenKeywords, entryKw, warnings, conceptPrereqMap) {
|
|
12012
12037
|
const concepts = [...new Set(nodeIds.flatMap((id) => nodeById.get(id)?.concept_codes || []))];
|
|
12013
12038
|
const newConcepts = concepts.filter((c) => !allSeenConcepts.has(c));
|
|
12014
12039
|
const knownConcepts = concepts.filter((c) => allSeenConcepts.has(c));
|
|
@@ -12027,7 +12052,15 @@ function checkSessionZpd(sessionIndex, nodeIds, nodeById, allSeenConcepts, allSe
|
|
|
12027
12052
|
if (sessionIndex > 0) {
|
|
12028
12053
|
if (concepts.length > 1 && knownConcepts.length === 0) {
|
|
12029
12054
|
if (verdict !== "TOO_MANY_NEW") verdict = "NO_ZPD_BRIDGE";
|
|
12030
|
-
|
|
12055
|
+
const seenList = [...allSeenConcepts];
|
|
12056
|
+
const prevConcept = seenList[seenList.length - 1];
|
|
12057
|
+
const bridgedByPrereq = prevConcept !== void 0 && concepts.some((c) => (conceptPrereqMap?.get(c) || []).includes(prevConcept));
|
|
12058
|
+
if (bridgedByPrereq) {
|
|
12059
|
+
verdict = verdict === "TOO_MANY_NEW" ? verdict : "OK";
|
|
12060
|
+
issues.push(`Bridge satisfied via prerequisite: prior session ended with recap of ${prevConcept}, a declared prerequisite of this session's concepts`);
|
|
12061
|
+
} else {
|
|
12062
|
+
issues.push("No known concepts from previous sessions to bridge new learning");
|
|
12063
|
+
}
|
|
12031
12064
|
} else if (concepts.length === 0 && allKeywords.length > 2 && knownKeywords.length === 0) {
|
|
12032
12065
|
if (verdict !== "TOO_MANY_NEW") verdict = "NO_ZPD_BRIDGE";
|
|
12033
12066
|
issues.push("No known keywords from previous sessions to bridge new learning");
|
|
@@ -12284,6 +12317,17 @@ async function buildCurriculumPlan(rawGraph, options) {
|
|
|
12284
12317
|
const allSeenConcepts = /* @__PURE__ */ new Set();
|
|
12285
12318
|
const allSeenKeywords = /* @__PURE__ */ new Set();
|
|
12286
12319
|
const conceptEncounterMap = /* @__PURE__ */ new Map();
|
|
12320
|
+
const conceptPrereqMap = /* @__PURE__ */ new Map();
|
|
12321
|
+
for (const e of graph.edges) {
|
|
12322
|
+
for (const to of nodeById.get(e.to)?.concept_codes || []) {
|
|
12323
|
+
for (const from of nodeById.get(e.from)?.concept_codes || []) {
|
|
12324
|
+
if (to === from) continue;
|
|
12325
|
+
const list = conceptPrereqMap.get(to) || [];
|
|
12326
|
+
if (!list.includes(from)) list.push(from);
|
|
12327
|
+
conceptPrereqMap.set(to, list);
|
|
12328
|
+
}
|
|
12329
|
+
}
|
|
12330
|
+
}
|
|
12287
12331
|
for (let i = 0; i < packed.length; i++) {
|
|
12288
12332
|
const s = packed[i];
|
|
12289
12333
|
for (const id of s.nodeIds) {
|
|
@@ -12337,7 +12381,7 @@ async function buildCurriculumPlan(rawGraph, options) {
|
|
|
12337
12381
|
const curCount = (conceptEncounterMap.get(cCode) || 0) + 1;
|
|
12338
12382
|
conceptEncounterMap.set(cCode, curCount);
|
|
12339
12383
|
}
|
|
12340
|
-
const zpdResult = checkSessionZpd(i, s.nodeIds, nodeById, allSeenConcepts, allSeenKeywords, entryKw, warnings);
|
|
12384
|
+
const zpdResult = checkSessionZpd(i, s.nodeIds, nodeById, allSeenConcepts, allSeenKeywords, entryKw, warnings, conceptPrereqMap);
|
|
12341
12385
|
const prereqDecisions = decidePrerequisites(ctx, nodeById, graph.edges, sessionIndexByNode, sessionOrderByIndex, constraints);
|
|
12342
12386
|
if (zpdResult.bridgeRepair) {
|
|
12343
12387
|
prereqDecisions.unshift(zpdResult.bridgeRepair);
|