@thanh01.pmt/domain-kit 0.3.0 → 0.5.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.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +42 -11
  3. package/dist/{chunk-MSRKUZLY.mjs → chunk-E4GCCWJO.mjs} +32 -2
  4. package/dist/chunk-E4GCCWJO.mjs.map +1 -0
  5. package/dist/{chunk-MCHHEBH3.mjs → chunk-G7HLFMD2.mjs} +5 -5
  6. package/dist/{chunk-MCHHEBH3.mjs.map → chunk-G7HLFMD2.mjs.map} +1 -1
  7. package/dist/{chunk-LKSVTRQE.mjs → chunk-TWKIVUTQ.mjs} +33 -3
  8. package/dist/chunk-TWKIVUTQ.mjs.map +1 -0
  9. package/dist/{chunk-Y7MRFFDJ.mjs → chunk-TYXSJUQU.mjs} +100 -9
  10. package/dist/chunk-TYXSJUQU.mjs.map +1 -0
  11. package/dist/curriculumFeedEmitter-T7NUBLAM.mjs +4 -0
  12. package/dist/{curriculumFeedEmitter-FYAWMPE6.mjs.map → curriculumFeedEmitter-T7NUBLAM.mjs.map} +1 -1
  13. package/dist/{curriculumFeedSchema-C8ZP3XMi.d.ts → curriculumFeedSchema-DUFwFyr2.d.cts} +196 -0
  14. package/dist/{curriculumFeedSchema-C8ZP3XMi.d.cts → curriculumFeedSchema-DUFwFyr2.d.ts} +196 -0
  15. package/dist/feed/index.cjs +60 -0
  16. package/dist/feed/index.cjs.map +1 -1
  17. package/dist/feed/index.d.cts +1 -1
  18. package/dist/feed/index.d.ts +1 -1
  19. package/dist/feed/index.mjs +2 -2
  20. package/dist/graph/index.cjs +98 -6
  21. package/dist/graph/index.cjs.map +1 -1
  22. package/dist/graph/index.d.cts +1 -1
  23. package/dist/graph/index.d.ts +1 -1
  24. package/dist/graph/index.mjs +1 -1
  25. package/dist/{hybridGraphPipeline-NZAWAPt_.d.ts → hybridGraphPipeline-DXQDbBEj.d.ts} +14 -2
  26. package/dist/{hybridGraphPipeline-BhFyh3cm.d.cts → hybridGraphPipeline-dT-bio-3.d.cts} +14 -2
  27. package/dist/index.cjs +158 -6
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +2 -2
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.mjs +4 -4
  32. package/dist/pipeline/index.cjs +157 -6
  33. package/dist/pipeline/index.cjs.map +1 -1
  34. package/dist/pipeline/index.d.cts +2 -2
  35. package/dist/pipeline/index.d.ts +2 -2
  36. package/dist/pipeline/index.mjs +4 -4
  37. package/dist/schemas/index.cjs +30 -0
  38. package/dist/schemas/index.cjs.map +1 -1
  39. package/dist/schemas/index.d.cts +1 -1
  40. package/dist/schemas/index.d.ts +1 -1
  41. package/dist/schemas/index.mjs +1 -1
  42. package/package.json +9 -10
  43. package/dist/chunk-LKSVTRQE.mjs.map +0 -1
  44. package/dist/chunk-MSRKUZLY.mjs.map +0 -1
  45. package/dist/chunk-Y7MRFFDJ.mjs.map +0 -1
  46. package/dist/curriculumFeedEmitter-FYAWMPE6.mjs +0 -4
@@ -1,4 +1,4 @@
1
- import { validateCurriculumFeed } from './chunk-MSRKUZLY.mjs';
1
+ import { validateCurriculumFeed } from './chunk-E4GCCWJO.mjs';
2
2
 
3
3
  // src/feed/curriculumFeedEmitter.ts
4
4
  var BLOOM_RANK = { Remember: 0, Understand: 1, Apply: 2, Analyze: 3, Evaluate: 4, Create: 5 };
@@ -43,6 +43,10 @@ function feedNodeFromStep(step, featureId) {
43
43
  references: arr(step.files).map((f) => ({ file: str(f), evidence: "step file" })).filter((f) => f.file),
44
44
  feature_id: featureId,
45
45
  concept_ref: null,
46
+ // P52/T2.1: carry the step's tangible outcome so the planner can cut
47
+ // sessions at user-visible checkpoints instead of raw minute counts.
48
+ user_visible_deliverable: str(step.outcome?.user_visible),
49
+ is_core: false,
46
50
  phase_id: "",
47
51
  introduce_aspect: null
48
52
  };
@@ -61,6 +65,20 @@ function deriveConceptDepthHint(concept) {
61
65
  if (str(concept.cio) || prereqs.length >= 1) return "cio";
62
66
  return "ulo";
63
67
  }
68
+ function deriveDepthInputs(minutes, depth) {
69
+ const ulo = Math.max(5, Math.round(minutes * 0.35));
70
+ const cio = Math.max(ulo + 5, Math.round(minutes * 0.65));
71
+ const sio = Math.max(cio + 5, minutes);
72
+ const variants = { ulo, cio, sio };
73
+ const candidates = [];
74
+ if (depth === "sio") {
75
+ candidates.push({ from_depth: "sio", to_depth: "cio", minutes_saved: sio - cio, reason: "SIO implementation practice deferred \u2014 CIO mechanism suffices for downstream consumers" });
76
+ }
77
+ if (depth === "cio" || depth === "sio") {
78
+ candidates.push({ from_depth: depth, to_depth: "ulo", minutes_saved: (depth === "sio" ? sio : cio) - ulo, reason: "Mechanism deferred \u2014 ULO awareness suffices (know-it-exists); never below the ULO floor" });
79
+ }
80
+ return { depth_variants: variants, depth_scaffold_candidates: candidates };
81
+ }
64
82
  function feedNodeFromConcept(concept) {
65
83
  const id = str(concept.id);
66
84
  const pts = arr(concept.problem_types).map((p) => str(p.bloom_level, "Understand")).filter((b) => b in BLOOM_RANK).sort((a, b) => BLOOM_RANK[a] - BLOOM_RANK[b]);
@@ -90,6 +108,15 @@ function feedNodeFromConcept(concept) {
90
108
  references,
91
109
  feature_id: null,
92
110
  concept_ref: id,
111
+ user_visible_deliverable: "",
112
+ // concepts are taught, not shipped — checkpoints live on product steps
113
+ ...deriveDepthInputs(Math.max(0, Math.round(num(concept.estimated_minutes, 15))), deriveConceptDepthHint(concept)),
114
+ is_core: false,
115
+ depth_context: {
116
+ ulo: str(concept.ulo),
117
+ cio: str(concept.cio),
118
+ sio: strs(concept.sio)
119
+ },
93
120
  phase_id: "",
94
121
  introduce_aspect: null
95
122
  };
@@ -196,6 +223,8 @@ function emitFromHybrid(hybrid, opts) {
196
223
  prevNode.concept_codes = [str(c.code) || cid];
197
224
  prevNode.keywords = { all: strs(c.keywords), new: [], prerequisite: [] };
198
225
  prevNode.scaffolding = { scaffold_candidates: [], core_value_minutes: 0 };
226
+ prevNode.depth_variants = { ulo: prevNode.estimated_minutes, cio: prevNode.estimated_minutes, sio: prevNode.estimated_minutes };
227
+ prevNode.depth_scaffold_candidates = [];
199
228
  nodes.push(prevNode);
200
229
  conceptOrder.push(prevNode.id);
201
230
  continue;
@@ -220,6 +249,7 @@ function emitFromHybrid(hybrid, opts) {
220
249
  node.name = str(c.name, cid) + " (advanced)";
221
250
  node.phase_id = pid;
222
251
  node.introduce_aspect = "advanced";
252
+ node.depth_scaffold_candidates = [{ from_depth: "sio", to_depth: "cio", minutes_saved: Math.round(node.estimated_minutes * 0.3), reason: "advanced revisit may drop to CIO recap without breaking first-teach chain" }];
223
253
  node.estimated_minutes = Math.max(15, Math.round(node.estimated_minutes * 0.5));
224
254
  node.concept_codes = [str(c.code) || cid];
225
255
  edges.push({ from: cid, to: revisitId, kind: "knowledge", reason: "advanced revisit of " + cid + " at phase " + pid });
@@ -440,5 +470,5 @@ function emitCurriculumFeed(graph, opts = {}) {
440
470
  }
441
471
 
442
472
  export { emitCurriculumFeed };
443
- //# sourceMappingURL=chunk-LKSVTRQE.mjs.map
444
- //# sourceMappingURL=chunk-LKSVTRQE.mjs.map
473
+ //# sourceMappingURL=chunk-TWKIVUTQ.mjs.map
474
+ //# sourceMappingURL=chunk-TWKIVUTQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/feed/curriculumFeedEmitter.ts"],"names":[],"mappings":";;;AAsBA,IAAM,UAAA,GAAqC,EAAE,QAAA,EAAU,CAAA,EAAG,UAAA,EAAY,CAAA,EAAG,KAAA,EAAO,CAAA,EAAG,OAAA,EAAS,CAAA,EAAG,QAAA,EAAU,CAAA,EAAG,QAAQ,CAAA,EAAE;AACtH,IAAM,IAAA,GAAO,CAAC,CAAA,KAAsB,MAAA,CAAO,CAAC,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AAG7D,IAAM,GAAA,GAAM,CAAC,CAAA,KAAuB,KAAA,CAAM,QAAQ,CAAC,CAAA,GAAK,IAAc,EAAC;AACvE,IAAM,GAAA,GAAM,CAAC,CAAA,EAAY,CAAA,GAAI,OAAgB,OAAO,CAAA,KAAM,WAAW,CAAA,GAAI,CAAA;AACzE,IAAM,GAAA,GAAM,CAAC,CAAA,EAAY,CAAA,KAAuB,OAAO,CAAA,KAAM,QAAA,IAAY,MAAA,CAAO,QAAA,CAAS,CAAC,CAAA,GAAI,CAAA,GAAI,CAAA;AAClG,IAAM,IAAA,GAAO,CAAC,CAAA,KAA0B,KAAA,CAAM,QAAQ,CAAC,CAAA,GAAI,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,KAAmB,OAAO,CAAA,KAAM,QAAQ,IAAI,EAAC;AAElH,SAAS,YAAY,IAAA,EAAmB;AACtC,EAAA,MAAM,MAAA,GAAU,IAAA,CAAK,MAAA,IAAU,EAAC;AAChC,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,MAAM,GAAA,CAAI,MAAA,CAAO,iBAAA,EAAmB,GAAA,CAAI,IAAA,CAAK,iBAAA,EAAmB,EAAE,CAAC,CAAC,CAAC,CAAA;AAC/F;AAEA,SAAS,WAAA,CAAY,MAAuB,OAAA,EAAuB;AACjE,EAAA,CAAC,KAAK,QAAA,KAAa,IAAA,CAAK,WAAW,EAAC,CAAA,EAAI,KAAK,OAAO,CAAA;AACtD;AAEA,SAAS,mBAAmB,KAAA,EAAyB;AAEnD,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,EAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACrB,IAAA,MAAM,GAAA,GAAM,EAAE,QAAA,CAAS,GAAA;AACvB,IAAA,CAAA,CAAE,QAAA,CAAS,MAAM,EAAC;AAClB,IAAA,CAAA,CAAE,QAAA,CAAS,eAAe,EAAC;AAC3B,IAAA,KAAA,MAAW,KAAK,GAAA,EAAK;AACnB,MAAA,IAAI,IAAA,CAAK,IAAI,CAAC,CAAA,IAAK,QAAA,CAAS,YAAA,CAAa,KAAK,CAAC,CAAA;AAAA,WAC1C,CAAA,CAAE,QAAA,CAAS,GAAA,CAAI,IAAA,CAAK,CAAC,CAAA;AAC1B,MAAA,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,IACZ;AAAA,EACF;AACF;AAEA,SAAS,gBAAA,CAAiB,MAAW,SAAA,EAA6B;AAChE,EAAA,MAAM,EAAA,GAAK,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA;AACtB,EAAA,OAAO;AAAA,IACL,EAAA;AAAA,IACA,IAAA,EAAM,cAAA;AAAA,IACN,IAAA,EAAM,GAAA,CAAI,IAAA,CAAK,IAAA,EAAM,EAAE,CAAA;AAAA,IACvB,aAAA,EAAe,IAAA,CAAK,IAAA,CAAK,aAAa,CAAA;AAAA,IACtC,QAAA,EAAU,EAAE,GAAA,EAAK,IAAA,CAAK,IAAA,CAAK,QAAQ,CAAA,EAAG,GAAA,EAAK,EAAC,EAAG,YAAA,EAAc,EAAC,EAAE;AAAA,IAChE,iBAAA,EAAmB,YAAY,IAAI,CAAA;AAAA,IACnC,UAAA,EAAY,OAAA;AAAA,IACZ,UAAA,EAAY,IAAA;AAAA,IACZ,gBAAA,EAAkB,UAAA;AAAA,IAClB,WAAA,EAAa,EAAE,mBAAA,EAAqB,IAAI,kBAAA,EAAoB,WAAA,CAAY,IAAI,CAAA,EAAE;AAAA,IAC9E,UAAA,EAAY,IAAI,IAAA,CAAK,KAAK,EAAE,GAAA,CAAI,CAAC,OAAO,EAAE,IAAA,EAAM,IAAI,CAAC,CAAA,EAAG,UAAU,WAAA,EAAY,CAAE,EAAE,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,IAAI,CAAA;AAAA,IACtG,UAAA,EAAY,SAAA;AAAA,IACZ,WAAA,EAAa,IAAA;AAAA;AAAA;AAAA,IAGb,wBAAA,EAA0B,GAAA,CAAK,IAAA,CAAK,OAAA,EAA6B,YAAY,CAAA;AAAA,IAC7E,OAAA,EAAS,KAAA;AAAA,IACT,QAAA,EAAU,EAAA;AAAA,IACV,gBAAA,EAAkB;AAAA,GACpB;AACF;AAEA,SAAS,uBAAuB,OAAA,EAAsC;AACpE,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,OAAA,CAAQ,UAAU,CAAA;AACvC,EAAA,IAAI,aAAa,KAAA,IAAS,QAAA,KAAa,KAAA,IAAS,QAAA,KAAa,OAAO,OAAO,QAAA;AAE3E,EAAA,MAAM,GAAA,GAAM,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC3B,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,OAAA,CAAQ,aAAa,CAAA;AAC1C,EAAA,MAAM,GAAA,GAAM,GAAA,CAAI,OAAA,CAAQ,aAAa,CAAA;AACrC,EAAA,MAAM,YAAA,GAAe,GAAA,CAAI,IAAA,CAAK,CAAC,CAAA,KAAM;AACnC,IAAA,MAAM,CAAA,GAAI,GAAA,CAAI,CAAA,CAAE,WAAW,CAAA;AAC3B,IAAA,OAAO,MAAM,OAAA,IAAW,CAAA,KAAM,SAAA,IAAa,CAAA,KAAM,cAAc,CAAA,KAAM,QAAA;AAAA,EACvE,CAAC,CAAA;AAED,EAAA,IAAI,GAAA,CAAI,MAAA,IAAU,CAAA,IAAK,YAAA,EAAc,OAAO,KAAA;AAC5C,EAAA,IAAI,IAAI,OAAA,CAAQ,GAAG,KAAK,OAAA,CAAQ,MAAA,IAAU,GAAG,OAAO,KAAA;AACpD,EAAA,OAAO,KAAA;AACT;AAQA,SAAS,iBAAA,CAAkB,SAAiB,KAAA,EAA+F;AACzI,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,OAAA,GAAU,IAAI,CAAC,CAAA;AAClD,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,CAAI,GAAA,GAAM,GAAG,IAAA,CAAK,KAAA,CAAM,OAAA,GAAU,IAAI,CAAC,CAAA;AACxD,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,GAAA,GAAM,GAAG,OAAO,CAAA;AACrC,EAAA,MAAM,QAAA,GAAW,EAAE,GAAA,EAAK,GAAA,EAAK,GAAA,EAAI;AACjC,EAAA,MAAM,aAAiE,EAAC;AACxE,EAAA,IAAI,UAAU,KAAA,EAAO;AACnB,IAAA,UAAA,CAAW,IAAA,CAAK,EAAE,UAAA,EAAY,KAAA,EAAO,QAAA,EAAU,KAAA,EAAO,aAAA,EAAe,GAAA,GAAM,GAAA,EAAK,MAAA,EAAQ,6FAAA,EAA0F,CAAA;AAAA,EACpL;AACA,EAAA,IAAI,KAAA,KAAU,KAAA,IAAS,KAAA,KAAU,KAAA,EAAO;AACtC,IAAA,UAAA,CAAW,IAAA,CAAK,EAAE,UAAA,EAAY,KAAA,EAAO,UAAU,KAAA,EAAO,aAAA,EAAA,CAAgB,KAAA,KAAU,KAAA,GAAQ,GAAA,GAAM,GAAA,IAAO,GAAA,EAAK,MAAA,EAAQ,gGAA2F,CAAA;AAAA,EAC/M;AACA,EAAA,OAAO,EAAE,cAAA,EAAgB,QAAA,EAAU,yBAAA,EAA2B,UAAA,EAAW;AAC3E;AAEA,SAAS,oBAAoB,OAAA,EAAwB;AACnD,EAAA,MAAM,EAAA,GAAK,GAAA,CAAI,OAAA,CAAQ,EAAE,CAAA;AACzB,EAAA,MAAM,GAAA,GAAM,GAAA,CAAI,OAAA,CAAQ,aAAa,CAAA,CAClC,GAAA,CAAI,CAAC,CAAA,KAAM,GAAA,CAAI,CAAA,CAAE,WAAA,EAAa,YAAY,CAAC,CAAA,CAC3C,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,IAAK,UAAU,CAAA,CAC7B,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,UAAA,CAAW,CAAC,CAAA,GAAI,UAAA,CAAW,CAAC,CAAC,CAAA;AAC/C,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,GAAA,CAAI,MAAA,GAAS,CAAC,CAAA,IAAK,YAAA;AAExC,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,OAAA,CAAQ,UAAU,CAAA;AACtC,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,MAAA,GAAS,CAAA,GAChC,OAAA,CAAQ,IAAI,CAAC,CAAA,MAAO,EAAE,IAAA,EAAM,GAAA,CAAI,CAAA,CAAE,MAAM,aAAa,CAAA,EAAG,QAAA,EAAU,GAAA,CAAI,CAAA,CAAE,QAAA,EAAU,IAAI,OAAA,CAAQ,IAAI,CAAC,CAAA,EAAE,CAAE,CAAA,CAAE,OAAO,CAAC,CAAA,KAAM,CAAA,CAAE,IAAI,CAAA,GAC7H,CAAC,EAAE,IAAA,EAAM,aAAA,EAAe,QAAA,EAAU,GAAA,CAAI,OAAA,CAAQ,QAAA,GAAW,wBAAwB,GAAA,CAAI,OAAA,CAAQ,QAAQ,CAAC,CAAA,CAAA,GAAK,GAAA,CAAI,QAAQ,IAAI,CAAA,EAAG,EAAE,CAAA,EAAG,CAAA;AAEvI,EAAA,MAAM,mBAAmB,GAAA,CAAI,OAAA,CAAQ,aAAa,CAAA,CAAE,GAAA,CAAI,CAAC,EAAA,MAAQ;AAAA,IAC/D,IAAA,EAAM,GAAA,CAAI,EAAA,CAAG,IAAA,EAAM,cAAc,CAAA;AAAA,IACjC,MAAA,EAAQ,GAAA,CAAI,EAAA,CAAG,WAAA,EAAa,0BAA0B,CAAA;AAAA,IACtD,aAAA,EAAe,GAAA,CAAI,EAAA,CAAG,iBAAA,EAAmB,EAAE,CAAA;AAAA,IAC3C,OAAA,EAAS;AAAA,GACX,CAAE,CAAA;AAEF,EAAA,OAAO;AAAA,IACL,EAAA;AAAA,IACA,IAAA,EAAM,SAAA;AAAA,IACN,IAAA,EAAM,GAAA,CAAI,OAAA,CAAQ,IAAA,EAAM,EAAE,CAAA;AAAA,IAC1B,aAAA,EAAe,CAAC,GAAA,CAAI,OAAA,CAAQ,IAAI,CAAA,IAAK,EAAE,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA;AAAA,IACvD,QAAA,EAAU,EAAE,GAAA,EAAK,IAAA,CAAK,OAAA,CAAQ,QAAQ,CAAA,EAAG,GAAA,EAAK,EAAC,EAAG,YAAA,EAAc,EAAC,EAAE;AAAA,IACnE,iBAAA,EAAmB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,OAAA,CAAQ,iBAAA,EAAmB,EAAE,CAAC,CAAC,CAAA;AAAA,IAC7E,UAAA,EAAY,QAAA;AAAA,IACZ,UAAA,EAAY,uBAAuB,OAAO,CAAA;AAAA,IAC1C,gBAAA,EAAkB,UAAA;AAAA,IAClB,WAAA,EAAa;AAAA,MACX,mBAAA,EAAqB,gBAAA;AAAA,MACrB,kBAAA,EAAoB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,OAAA,CAAQ,iBAAA,EAAmB,EAAE,CAAC,CAAC;AAAA,KAChF;AAAA,IACA,UAAA;AAAA,IACA,UAAA,EAAY,IAAA;AAAA,IACZ,WAAA,EAAa,EAAA;AAAA,IACb,wBAAA,EAA0B,EAAA;AAAA;AAAA,IAC1B,GAAG,iBAAA,CAAkB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,GAAA,CAAI,OAAA,CAAQ,iBAAA,EAAmB,EAAE,CAAC,CAAC,CAAA,EAAG,sBAAA,CAAuB,OAAO,CAAC,CAAA;AAAA,IACjH,OAAA,EAAS,KAAA;AAAA,IACT,aAAA,EAAe;AAAA,MACb,GAAA,EAAK,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAAA,MACpB,GAAA,EAAK,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAAA,MACpB,GAAA,EAAK,IAAA,CAAK,OAAA,CAAQ,GAAG;AAAA,KACvB;AAAA,IACA,QAAA,EAAU,EAAA;AAAA,IACV,gBAAA,EAAkB;AAAA,GACpB;AACF;AAEA,SAAS,eAAA,CACP,CAAA,EAAQ,GAAA,EAAa,KAAA,EAAmB,OAAmB,SAAA,EACjD;AACV,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,CAAA,CAAE,KAAK,CAAA;AACzB,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,IAAI,IAAA,GAAO,EAAA;AACX,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,IAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,KAAA,CAAM,CAAC,GAAG,GAAG,CAAA;AAC/C,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAClB,IAAA,OAAA,CAAQ,IAAA,CAAK,SAAS,EAAE,CAAA;AACxB,IAAA,KAAA,CAAM,KAAK,QAAQ,CAAA;AACnB,IAAA,IAAI,IAAA,EAAM,KAAA,CAAM,IAAA,CAAK,EAAE,IAAA,EAAM,IAAA,EAAM,EAAA,EAAI,QAAA,CAAS,EAAA,EAAI,IAAA,EAAM,MAAA,EAAQ,MAAA,EAAQ,uBAAuB,CAAA;AACjG,IAAA,IAAA,GAAO,QAAA,CAAS,EAAA;AAAA,EAClB;AACA,EAAA,KAAA,MAAW,GAAA,IAAO,IAAA,CAAK,CAAA,CAAE,UAAU,CAAA,EAAG;AACpC,IAAA,MAAM,WAAW,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,eAAe,GAAG,CAAA;AACvD,IAAA,IAAI,YAAY,OAAA,CAAQ,CAAC,GAAG,KAAA,CAAM,IAAA,CAAK,EAAE,IAAA,EAAM,QAAA,CAAS,IAAI,EAAA,EAAI,OAAA,CAAQ,CAAC,CAAA,EAAG,IAAA,EAAM,QAAQ,MAAA,EAAQ,qBAAA,GAAwB,KAAK,CAAA;AAAA,EACjI;AACA,EAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG,SAAA,CAAU,KAAK,EAAE,EAAA,EAAI,IAAA,GAAO,GAAA,EAAK,IAAA,EAAM,GAAA,CAAI,EAAE,IAAA,EAAM,GAAG,CAAA,EAAG,QAAA,EAAU,OAAA,EAAS,SAAA,EAAW,IAAI,CAAA,CAAE,WAAA,EAAa,EAAE,CAAA,EAAG,CAAA;AACvI,EAAA,OAAO,OAAA;AACT;AAoBA,SAAS,cAAA,CAAe,QAAa,IAAA,EAAuC;AAC1E,EAAA,MAAM,QAAoB,EAAC;AAC3B,EAAA,MAAM,QAAoB,EAAC;AAC3B,EAAA,MAAM,YAA4B,EAAC;AACnC,EAAA,MAAM,EAAA,GAAM,MAAA,CAAO,aAAA,IAAiB,EAAC;AACrC,EAAA,MAAM,EAAA,GAAM,MAAA,CAAO,eAAA,IAAmB,EAAC;AACvC,EAAA,MAAM,SAAA,GAAY,GAAA,CAAI,MAAA,CAAO,MAAM,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,KAAM,GAAA,CAAI,CAAA,CAAE,EAAE,CAAC,CAAA;AAE5D,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,EAAA,CAAG,QAAQ,CAAA;AAChC,EAAA,MAAM,WAAA,GAAc,GAAA,CAAI,EAAA,CAAG,QAAQ,CAAA;AACnC,EAAA,MAAM,WAAA,GAAc,IAAI,GAAA,CAAI,WAAA,CAAY,IAAI,CAAC,CAAA,KAAM,CAAC,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA,EAAG,CAAC,CAAC,CAAC,CAAA;AAGlE,EAAA,IAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAC1B,IAAA,KAAA,MAAW,KAAK,WAAA,EAAa;AAC3B,MAAA,MAAM,IAAA,GAAO,oBAAoB,CAAC,CAAA;AAClC,MAAA,KAAA,CAAM,KAAK,IAAI,CAAA;AACf,MAAA,KAAA,MAAW,KAAK,IAAA,CAAK,CAAA,CAAE,aAAa,CAAA,QAAS,IAAA,CAAK,EAAE,IAAA,EAAM,CAAA,EAAG,IAAI,IAAA,CAAK,EAAA,EAAI,MAAM,WAAA,EAAa,MAAA,EAAQ,wBAAwB,CAAA;AAAA,IAC/H;AACA,IAAA,QAAA,CAAS,QAAQ,CAAC,CAAA,EAAG,OAAO,eAAA,CAAgB,CAAA,EAAG,IAAI,CAAA,CAAE,EAAA,EAAI,GAAA,GAAM,IAAA,CAAK,KAAK,CAAC,CAAC,GAAG,KAAA,EAAO,KAAA,EAAO,SAAS,CAAC,CAAA;AACtG,IAAA,wBAAA,CAAyB,MAAA,EAAQ,KAAA,EAAO,KAAW,CAAA;AACnD,IAAA,kBAAA,CAAmB,KAAK,CAAA;AACxB,IAAA,OAAO,sBAAA,CAAuB;AAAA,MAC5B,cAAA,EAAgB,CAAA;AAAA,MAChB,MAAA,EAAQ,EAAE,UAAA,EAAY,cAAA,EAAgB,QAAA,EAAU,IAAA,CAAK,QAAA,IAAY,EAAC,EAAG,mBAAA,EAAqB,IAAA,CAAK,kBAAA,IAAsB,CAAA,EAAE;AAAA,MACvH,cAAA,EAAgB,KAAA;AAAA,MAChB,gBAAA,EAAkB,KAAA;AAAA,MAClB,mBAAA,EAAqB,SAAA;AAAA,MACrB,QAAQ;AAAC,KACV,CAAA;AAAA,EACH;AAGA,EAAA,MAAM,YAAA,uBAAmB,GAAA,EAAoB;AAC7C,EAAA,QAAA,CAAS,QAAQ,CAAC,CAAA,EAAG,EAAA,KAAO,YAAA,CAAa,IAAI,GAAA,CAAI,CAAA,CAAE,EAAA,EAAI,GAAA,GAAM,KAAK,EAAA,GAAK,CAAC,CAAC,CAAA,EAAG,EAAE,CAAC,CAAA;AAC/E,EAAA,MAAM,gBAAA,uBAAuB,GAAA,EAAsB;AACnD,EAAA,KAAA,MAAW,KAAK,QAAA,EAAU;AACxB,IAAA,MAAM,GAAA,GAAM,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA;AACpB,IAAA,gBAAA,CAAiB,IAAI,GAAA,EAAK,GAAA,CAAI,CAAA,CAAE,KAAK,EAAE,GAAA,CAAI,CAAC,CAAA,KAAM,GAAA,CAAI,EAAE,EAAE,CAAC,CAAA,CAAE,MAAA,CAAO,OAAO,CAAC,CAAA;AAAA,EAC9E;AAEA,EAAA,MAAM,UAA0B,EAAC;AACjC,EAAA,MAAM,eAAA,uBAAsB,GAAA,EAAY;AACxC,EAAA,MAAM,eAAA,uBAAsB,GAAA,EAAY;AACxC,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,MAAA,CAAO,KAAK,CAAA;AAEjC,EAAA,KAAA,MAAW,MAAM,SAAA,EAAW;AAC1B,IAAA,MAAM,GAAA,GAAM,GAAA,CAAI,EAAA,CAAG,EAAE,CAAA;AACrB,IAAA,MAAM,QAAQ,GAAA,CAAI,EAAA,CAAG,KAAA,EAAO,OAAA,CAAQ,SAAS,CAAC,CAAA;AAC9C,IAAA,MAAM,eAAyB,EAAC;AAChC,IAAA,MAAM,MAAA,GAAS,GAAA,CAAI,EAAA,CAAG,UAAU,CAAA;AAGhC,IAAA,MAAM,IAAA,GAAO,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA,KAAM,IAAI,CAAA,CAAE,MAAA,EAAQ,KAAK,CAAA,KAAM,KAAK,CAAA;AAChE,IAAA,MAAM,QAAA,GAAW,OAAO,MAAA,CAAO,CAAC,MAAM,GAAA,CAAI,CAAA,CAAE,MAAM,CAAA,KAAM,UAAU,CAAA;AAClE,IAAA,MAAM,QAAA,GAAW,OAAO,MAAA,CAAO,CAAC,MAAM,GAAA,CAAI,CAAA,CAAE,MAAM,CAAA,KAAM,SAAS,CAAA;AACjE,IAAA,KAAA,MAAW,KAAA,IAAS,CAAC,GAAG,IAAA,EAAM,GAAG,QAAA,EAAU,GAAG,QAAQ,CAAA,EAAG;AACvD,MAAA,MAAM,GAAA,GAAM,GAAA,CAAI,KAAA,CAAM,UAAU,CAAA;AAChC,MAAA,MAAM,CAAA,GAAI,WAAA,CAAY,GAAA,CAAI,GAAG,CAAA;AAC7B,MAAA,IAAI,CAAC,CAAA,EAAG;AACN,QAAA,MAAM,IAAI,KAAA,CAAM,QAAA,GAAW,GAAA,GAAM,8BAAA,GAAiC,MAAM,gBAAgB,CAAA;AAAA,MAC1F;AACA,MAAA,IAAI,GAAA,CAAI,KAAA,CAAM,MAAA,EAAQ,KAAK,CAAA,KAAM,cAAc,CAAC,eAAA,CAAgB,GAAA,CAAI,GAAG,CAAA,EAAG;AACxE,QAAA,MAAM,IAAI,KAAA,CAAM,QAAA,GAAW,GAAA,GAAM,6BAAA,GAAgC,MAAM,uCAAuC,CAAA;AAAA,MAChH;AACA,MAAA,IAAI,GAAA,CAAI,KAAA,CAAM,MAAA,EAAQ,KAAK,MAAM,SAAA,EAAW;AAC1C,QAAA,IAAI,eAAA,CAAgB,GAAA,CAAI,GAAG,CAAA,EAAG;AAC5B,UAAA,MAAM,IAAI,KAAA,CAAM,QAAA,GAAW,GAAA,GAAM,oBAAA,GAAuB,MAAM,oFAAoF,CAAA;AAAA,QACpJ;AACA,QAAA,IAAI,eAAA,CAAgB,GAAA,CAAI,GAAG,CAAA,EAAG;AAC5B,UAAA,MAAM,IAAI,KAAA,CAAM,QAAA,GAAW,GAAA,GAAM,gCAAA,GAAmC,MAAM,gBAAgB,CAAA;AAAA,QAC5F;AACA,QAAA,MAAM,MAAA,GAAS,UAAU,SAAA,CAAU,CAAC,QAAQ,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA,KAAM,GAAG,CAAA;AAC/D,QAAA,MAAM,SAAA,GAAY,UAAU,SAAA,CAAU,CAAC,QAAQ,GAAA,CAAI,GAAA,CAAI,UAAU,CAAA,CAAE,IAAA,CAAK,CAAC,OAAO,GAAA,CAAI,EAAA,CAAG,UAAU,CAAA,KAAM,GAAA,IAAO,GAAA,CAAI,GAAG,MAAA,EAAQ,KAAK,CAAA,KAAM,SAAS,CAAC,CAAA;AAClJ,QAAA,IAAI,YAAY,CAAA,EAAG;AACjB,UAAA,MAAM,IAAI,KAAA,CAAM,QAAA,GAAW,GAAA,GAAM,oBAAA,GAAuB,MAAM,4DAA4D,CAAA;AAAA,QAC5H;AACA,QAAA,IAAI,UAAU,SAAA,EAAW;AACvB,UAAA,MAAM,IAAI,KAAA,CAAM,QAAA,GAAW,GAAA,GAAM,oBAAA,GAAuB,MAAM,kEAAkE,CAAA;AAAA,QAClI;AACA,QAAA,eAAA,CAAgB,IAAI,GAAG,CAAA;AAOvB,QAAA,MAAM,QAAA,GAAW,oBAAoB,CAAC,CAAA;AACtC,QAAA,QAAA,CAAS,EAAA,GAAK,GAAA,GAAM,QAAA,GAAW,IAAA,CAAK,KAAK,CAAA;AACzC,QAAA,QAAA,CAAS,IAAA,GAAO,GAAA,CAAI,CAAA,CAAE,IAAA,EAAM,GAAG,CAAA,GAAI,YAAA;AACnC,QAAA,QAAA,CAAS,QAAA,GAAW,GAAA;AACpB,QAAA,QAAA,CAAS,gBAAA,GAAmB,SAAA;AAC5B,QAAA,QAAA,CAAS,iBAAA,GAAoB,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,QAAA,CAAS,iBAAA,GAAoB,IAAI,CAAC,CAAA;AACtF,QAAA,QAAA,CAAS,UAAA,GAAa,KAAA;AACtB,QAAA,QAAA,CAAS,UAAA,GAAa,YAAA;AACtB,QAAA,QAAA,CAAS,gBAAgB,CAAC,GAAA,CAAI,CAAA,CAAE,IAAI,KAAK,GAAG,CAAA;AAC5C,QAAA,QAAA,CAAS,QAAA,GAAW,EAAE,GAAA,EAAK,IAAA,CAAK,CAAA,CAAE,QAAQ,CAAA,EAAG,GAAA,EAAK,EAAC,EAAG,YAAA,EAAc,EAAC,EAAE;AACvE,QAAA,QAAA,CAAS,cAAc,EAAE,mBAAA,EAAqB,EAAC,EAAG,oBAAoB,CAAA,EAAE;AACxE,QAAA,QAAA,CAAS,cAAA,GAAiB,EAAE,GAAA,EAAK,QAAA,CAAS,iBAAA,EAAmB,KAAK,QAAA,CAAS,iBAAA,EAAmB,GAAA,EAAK,QAAA,CAAS,iBAAA,EAAkB;AAC9H,QAAA,QAAA,CAAS,4BAA4B,EAAC;AACtC,QAAA,KAAA,CAAM,KAAK,QAAQ,CAAA;AACnB,QAAA,YAAA,CAAa,IAAA,CAAK,SAAS,EAAE,CAAA;AAC7B,QAAA;AAAA,MACF;AACA,MAAA,IAAI,CAAC,eAAA,CAAgB,GAAA,CAAI,GAAG,CAAA,EAAG;AAC7B,QAAA,MAAM,IAAA,GAAO,oBAAoB,CAAC,CAAA;AAClC,QAAA,IAAA,CAAK,QAAA,GAAW,GAAA;AAChB,QAAA,IAAA,CAAK,gBAAA,GAAmB,KAAA;AACxB,QAAA,KAAA,CAAM,KAAK,IAAI,CAAA;AACf,QAAA,KAAA,MAAW,CAAA,IAAK,IAAA,CAAK,CAAA,CAAE,aAAa,CAAA,EAAG;AACrC,UAAA,IAAI,CAAC,eAAA,CAAgB,GAAA,CAAI,CAAC,CAAA,EAAG;AAC3B,YAAA,MAAM,IAAI,MAAM,UAAA,GAAa,GAAA,GAAM,aAAa,GAAA,GAAM,qBAAA,GAAwB,IAAI,2DAA2D,CAAA;AAAA,UAC/I;AACA,UAAA,KAAA,CAAM,IAAA,CAAK,EAAE,IAAA,EAAM,CAAA,EAAG,EAAA,EAAI,KAAK,IAAA,EAAM,WAAA,EAAa,MAAA,EAAQ,sBAAA,EAAwB,CAAA;AAAA,QACpF;AACA,QAAA,eAAA,CAAgB,IAAI,GAAG,CAAA;AACvB,QAAA,YAAA,CAAa,KAAK,GAAG,CAAA;AAAA,MACvB,CAAA,MAAO;AAIL,QAAA,MAAM,SAAA,GAAY,GAAA,GAAM,OAAA,GAAU,IAAA,CAAK,KAAK,CAAA;AAC5C,QAAA,MAAM,IAAA,GAAO,oBAAoB,CAAC,CAAA;AAClC,QAAA,IAAA,CAAK,EAAA,GAAK,SAAA;AACV,QAAA,IAAA,CAAK,IAAA,GAAO,GAAA,CAAI,CAAA,CAAE,IAAA,EAAM,GAAG,CAAA,GAAI,aAAA;AAC/B,QAAA,IAAA,CAAK,QAAA,GAAW,GAAA;AAChB,QAAA,IAAA,CAAK,gBAAA,GAAmB,UAAA;AACxB,QAAA,IAAA,CAAK,4BAA4B,CAAC,EAAE,UAAA,EAAY,KAAA,EAAO,UAAU,KAAA,EAAO,aAAA,EAAe,IAAA,CAAK,KAAA,CAAM,KAAK,iBAAA,GAAoB,GAAG,CAAA,EAAG,MAAA,EAAQ,6EAA6E,CAAA;AACtN,QAAA,IAAA,CAAK,iBAAA,GAAoB,KAAK,GAAA,CAAI,EAAA,EAAI,KAAK,KAAA,CAAM,IAAA,CAAK,iBAAA,GAAoB,GAAG,CAAC,CAAA;AAC9E,QAAA,IAAA,CAAK,gBAAgB,CAAC,GAAA,CAAI,CAAA,CAAE,IAAI,KAAK,GAAG,CAAA;AACxC,QAAA,KAAA,CAAM,IAAA,CAAK,EAAE,IAAA,EAAM,GAAA,EAAK,EAAA,EAAI,SAAA,EAAW,IAAA,EAAM,WAAA,EAAa,MAAA,EAAQ,sBAAA,GAAyB,GAAA,GAAM,YAAA,GAAe,KAAK,CAAA;AACrH,QAAA,KAAA,CAAM,KAAK,IAAI,CAAA;AACf,QAAA,YAAA,CAAa,KAAK,SAAS,CAAA;AAAA,MAC7B;AAAA,IACF;AACA,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,EAAA,CAAG,WAAW,CAAA,CACnC,OAAO,CAAC,GAAA,KAAQ,YAAA,CAAa,GAAA,CAAI,GAAG,CAAC,EACrC,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,YAAA,CAAa,GAAA,CAAI,CAAC,CAAA,GAAK,YAAA,CAAa,GAAA,CAAI,CAAC,CAAE,CAAA;AAC7D,IAAA,OAAA,CAAQ,KAAK,EAAE,EAAA,EAAI,KAAK,KAAA,EAAO,IAAA,EAAM,IAAI,EAAA,CAAG,IAAA,EAAM,GAAG,CAAA,EAAG,mBAAmB,GAAA,CAAI,EAAA,CAAG,kBAAkB,CAAA,EAAG,YAAA,EAAc,YAAY,CAAA;AAAA,EACnI;AAGA,EAAA,KAAA,MAAW,KAAK,QAAA,EAAU;AACxB,IAAA,MAAM,GAAA,GAAM,GAAA,CAAI,CAAA,CAAE,UAAU,CAAA;AAC5B,IAAA,IAAI,GAAA,IAAO,YAAY,GAAA,CAAI,GAAG,KAAK,CAAC,eAAA,CAAgB,GAAA,CAAI,GAAG,CAAA,EAAG;AAC5D,MAAA,MAAM,IAAI,KAAA,CAAM,UAAA,GAAa,GAAA,GAAM,uEAAuE,CAAA;AAAA,IAC5G;AAAA,EACF;AAEA,EAAA,MAAM,YAAA,GAAe,IAAI,GAAA,CAAI,OAAA,CAAQ,QAAQ,CAAC,CAAA,KAAM,CAAA,CAAE,UAAU,CAAC,CAAA;AACjE,EAAA,MAAM,kBAAkB,QAAA,CAAS,GAAA,CAAI,CAAC,CAAA,KAAM,GAAA,CAAI,EAAE,EAAE,CAAC,CAAA,CAAE,MAAA,CAAO,CAAC,GAAA,KAAQ,GAAA,IAAO,CAAC,YAAA,CAAa,GAAA,CAAI,GAAG,CAAC,CAAA;AACpG,EAAA,IAAI,eAAA,CAAgB,SAAS,CAAA,EAAG;AAC9B,IAAA,MAAM,IAAI,KAAA,CAAM,qCAAA,GAAwC,gBAAgB,IAAA,CAAK,IAAI,IAAI,gBAAgB,CAAA;AAAA,EACvG;AAGA,EAAA,MAAM,eAA2B,EAAC;AAClC,EAAA,KAAA,MAAW,OAAO,OAAA,EAAS;AACzB,IAAA,MAAM,GAAA,GAAgB,CAAC,GAAG,GAAA,CAAI,YAAY,CAAA;AAC1C,IAAA,KAAA,MAAW,GAAA,IAAO,IAAI,UAAA,EAAY;AAChC,MAAA,MAAM,CAAA,GAAI,SAAS,IAAA,CAAK,CAAC,MAAM,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA,KAAM,GAAG,CAAA;AAChD,MAAA,MAAM,UAAU,eAAA,CAAgB,CAAA,EAAG,GAAA,EAAK,KAAA,EAAO,OAAO,SAAS,CAAA;AAC/D,MAAA,KAAA,MAAW,OAAO,OAAA,EAAS;AACzB,QAAA,MAAM,OAAO,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,GAAG,CAAA;AAC3C,QAAA,IAAA,CAAK,WAAW,GAAA,CAAI,EAAA;AAAA,MACtB;AACA,MAAA,GAAA,CAAI,IAAA,CAAK,GAAG,OAAO,CAAA;AAAA,IACrB;AACA,IAAA,YAAA,CAAa,KAAK,GAAG,CAAA;AAAA,EACvB;AASA,EAAA,MAAM,MAAA,GAAS,IAAI,GAAA,CAAY,YAAA,CAAa,MAAM,CAAA;AAClD,EAAA,MAAM,eAA2B,EAAC;AAClC,EAAA,KAAA,MAAW,OAAO,YAAA,EAAc;AAC9B,IAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,MAAA,MAAM,IAAI,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,EAAE,CAAA;AACvC,MAAA,IAAI,CAAA,EAAG,YAAA,CAAa,IAAA,CAAK,CAAC,CAAA;AAAA,IAC5B;AAAA,EACF;AACA,EAAA,KAAA,MAAW,CAAA,IAAK,KAAA,EAAO,IAAI,CAAC,MAAA,CAAO,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA,EAAG,YAAA,CAAa,IAAA,CAAK,CAAC,CAAA;AACjE,EAAA,KAAA,CAAM,MAAA,GAAS,CAAA;AACf,EAAA,KAAA,CAAM,IAAA,CAAK,GAAG,YAAY,CAAA;AAI1B,EAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,GAAO,GAAA,GAAM,CAAA,CAAE,EAAE,CAAC,CAAA;AAC9D,EAAA,MAAM,QAAA,GAAW,CAAC,IAAA,EAAc,EAAA,EAAY,MAAA,KAAmB;AAC7D,IAAA,MAAM,GAAA,GAAM,OAAO,GAAA,GAAM,EAAA;AACzB,IAAA,IAAI,QAAA,CAAS,GAAA,CAAI,GAAG,CAAA,EAAG;AACvB,IAAA,QAAA,CAAS,IAAI,GAAG,CAAA;AAChB,IAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,IAAI,IAAA,EAAM,WAAA,EAAa,QAAQ,CAAA;AAAA,EACpD,CAAA;AACA,EAAA,KAAA,MAAW,KAAK,QAAA,EAAU;AACxB,IAAA,MAAM,GAAA,GAAM,GAAA,CAAI,CAAA,CAAE,UAAU,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,GAAA,CAAI,CAAA,CAAE,UAAU,CAAA;AAC5B,IAAA,IAAI,CAAC,WAAA,CAAY,GAAA,CAAI,GAAG,CAAA,EAAG;AAC3B,IAAA,MAAM,aAAa,gBAAA,CAAiB,GAAA,CAAI,GAAG,CAAA,IAAK,IAAI,CAAC,CAAA;AACrD,IAAA,IAAI,CAAC,SAAA,EAAW;AAChB,IAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,CAAA,CAAE,KAAA,EAAO,KAAK,CAAA;AAKhC,IAAA,MAAM,UAAA,GAAa,KAAA,CAAM,IAAA,CAAK,CAAC,EAAA,KAAO,EAAA,CAAG,IAAA,KAAS,SAAA,IAAa,EAAA,CAAG,WAAA,KAAgB,GAAA,IAAO,EAAA,CAAG,qBAAqB,KAAK,CAAA;AACtH,IAAA,MAAM,WAAW,KAAA,CAAM,IAAA,CAAK,CAAC,EAAA,KAAO,EAAA,CAAG,OAAO,SAAS,CAAA;AACvD,IAAA,MAAM,cAAA,GAAiB,UAAA,GAAa,OAAA,CAAQ,SAAA,CAAU,CAAC,OAAO,EAAA,CAAG,EAAA,KAAO,UAAA,CAAW,QAAQ,CAAA,GAAI,EAAA;AAC/F,IAAA,MAAM,YAAA,GAAe,QAAA,GAAW,OAAA,CAAQ,SAAA,CAAU,CAAC,OAAO,EAAA,CAAG,EAAA,KAAO,QAAA,CAAS,QAAQ,CAAA,GAAI,EAAA;AACzF,IAAA,IAAI,OAAA,CAAQ,MAAA,GAAS,CAAA,IAAK,cAAA,GAAiB,YAAA,EAAc;AACvD,MAAA,WAAA,CAAY,IAAA,EAAM,6BAAA,GAAgC,GAAA,GAAM,kDAAA,GAAqD,YAAY,oCAAoC,CAAA;AAC7J,MAAA;AAAA,IACF;AACA,IAAA,QAAA,CAAS,GAAA,EAAK,WAAW,GAAA,CAAI,CAAA,CAAE,cAAc,aAAa,CAAA,GAAI,IAAA,GAAO,KAAA,GAAQ,GAAG,CAAA;AAIhF,IAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACrB,MAAA,IAAI,CAAA,CAAE,IAAA,KAAS,SAAA,IAAa,CAAA,CAAE,gBAAgB,GAAA,IAAO,CAAA,CAAE,gBAAA,KAAqB,UAAA,IAAc,CAAA,CAAE,QAAA,KAAa,GAAA,CAAI,CAAA,CAAE,QAAQ,CAAA,EAAG;AACxH,QAAA,QAAA,CAAS,CAAA,CAAE,EAAA,EAAI,SAAA,EAAW,wBAAA,GAA2B,QAAQ,GAAG,CAAA;AAAA,MAClE;AAAA,IACF;AAAA,EACF;AAEA,EAAA,kBAAA,CAAmB,KAAK,CAAA;AAQxB,EAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAAI,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,KAAM,CAAC,CAAA,CAAE,EAAA,EAAI,CAAC,CAAC,CAAC,CAAA;AACxD,EAAA,MAAM,UAAA,uBAAiB,GAAA,EAAoB;AAC3C,EAAA,KAAA,MAAW,CAAA,IAAK,OAAO,KAAA,MAAW,CAAA,IAAK,EAAE,QAAA,EAAU,GAAA,IAAO,EAAC,EAAG;AAC5D,IAAA,MAAM,GAAA,GAAM,EAAE,WAAA,EAAY;AAC1B,IAAA,IAAI,CAAC,WAAW,GAAA,CAAI,GAAG,GAAG,UAAA,CAAW,GAAA,CAAI,GAAA,EAAK,CAAA,CAAE,EAAE,CAAA;AAAA,EACpD;AACA,EAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACrB,IAAA,KAAA,MAAW,CAAA,IAAK,CAAA,CAAE,QAAA,EAAU,YAAA,IAAgB,EAAC,EAAG;AAC9C,MAAA,MAAM,GAAA,GAAM,UAAA,CAAW,GAAA,CAAI,CAAA,CAAE,aAAa,CAAA;AAC1C,MAAA,IAAI,CAAC,GAAA,IAAO,GAAA,KAAQ,CAAA,CAAE,EAAA,EAAI;AAC1B,MAAA,MAAM,UAAU,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,GAAG,CAAA;AAS9C,MAAA,MAAM,OAAA,GAAU,CAAC,EAAA,KAAuB;AACtC,QAAA,MAAM,KAAK,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,EAAE,CAAA;AACxC,QAAA,OAAO,EAAA,GAAK,QAAQ,SAAA,CAAU,CAAC,MAAM,CAAA,CAAE,EAAA,KAAO,EAAA,CAAG,QAAQ,CAAA,GAAI,EAAA;AAAA,MAC/D,CAAA;AACA,MAAA,MAAM,gBAAA,GAAmB,CAAA,CAAE,IAAA,KAAS,SAAA,IAAa,SAAS,IAAA,KAAS,cAAA;AACnE,MAAA,MAAM,SAAA,GAAY,QAAQ,GAAG,CAAA;AAC7B,MAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,CAAA,CAAE,EAAE,CAAA;AAC5B,MAAA,IAAI,gBAAA,IAAqB,OAAA,CAAQ,MAAA,GAAS,CAAA,IAAK,YAAY,OAAA,EAAU;AACnE,QAAA,WAAA;AAAA,UAAY,IAAA;AAAA,UACV,qCAAqC,GAAA,GAAM,uCAAA,GAA0C,CAAA,CAAE,EAAA,GACvF,gBAAgB,CAAA,GAAI;AAAA,SACtB;AACA,QAAA;AAAA,MACF;AACA,MAAA,IAAI,SAAA,CAAU,GAAA,CAAI,GAAG,CAAA,GAAK,UAAU,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA,WAAa,GAAA,EAAK,CAAA,CAAE,EAAA,EAAI,wBAAA,GAA2B,IAAI,GAAG,CAAA;AAAA,IACxG;AAAA,EACF;AAEA,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,GAAA,CAAI,CAAC,KAAK,CAAA,MAAO;AAAA,IACtC,IAAI,GAAA,CAAI,EAAA;AAAA,IACR,OAAO,GAAA,CAAI,KAAA;AAAA,IACX,MAAM,GAAA,CAAI,IAAA;AAAA,IACV,kBAAA,EAAoB,GAAA,CAAI,iBAAA,IAAqB,QAAA,IAAY,IAAI,CAAA,CAAA,GAAK,cAAA;AAAA,IAClE,QAAA,EAAU,aAAa,CAAC;AAAA,GAC1B,CAAE,CAAA;AAEF,EAAA,OAAO,sBAAA,CAAuB;AAAA,IAC5B,cAAA,EAAgB,CAAA;AAAA,IAChB,MAAA,EAAQ,EAAE,UAAA,EAAY,cAAA,EAAgB,QAAA,EAAU,IAAA,CAAK,QAAA,IAAY,EAAC,EAAG,mBAAA,EAAqB,IAAA,CAAK,kBAAA,IAAsB,CAAA,EAAE;AAAA,IACvH,cAAA,EAAgB,KAAA;AAAA,IAChB,gBAAA,EAAkB,KAAA;AAAA,IAClB,mBAAA,EAAqB,SAAA;AAAA,IACrB;AAAA,GACD,CAAA;AACH;AAGA,SAAS,wBAAA,CAAyB,MAAA,EAAa,KAAA,EAAmB,KAAA,EAAmB,YAAA,EAA6B;AAChH,EAAA,MAAM,aAAa,IAAI,GAAA,CAAI,MAAM,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,IAAA,KAAS,SAAS,CAAA,CAAE,IAAI,CAAC,CAAA,KAAM,EAAE,WAAA,IAAe,CAAA,CAAE,EAAE,CAAC,CAAA;AACtG,EAAA,KAAA,MAAW,CAAA,IAAK,GAAA,CAAI,MAAA,CAAO,KAAK,CAAA,EAAG;AACjC,IAAA,MAAM,SAAA,GAAY,GAAA,CAAI,CAAA,CAAE,UAAU,CAAA;AAClC,IAAA,MAAM,SAAA,GAAY,GAAA,CAAI,CAAA,CAAE,UAAU,CAAA;AAClC,IAAA,IAAI,CAAC,UAAA,CAAW,GAAA,CAAI,SAAS,CAAA,EAAG;AAChC,IAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,CAAA,CAAE,KAAA,EAAO,KAAK,CAAA;AAChC,IAAA,MAAM,YAAY,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,eAAe,SAAS,CAAA;AAC9D,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,WAAW,EAAA,EAAI,SAAA,CAAU,IAAI,IAAA,EAAM,WAAA,EAAa,MAAA,EAAQ,GAAA,CAAI,EAAE,YAAA,EAAc,aAAa,IAAI,IAAA,GAAO,KAAA,GAAQ,KAAK,CAAA;AACpI,MAAA,IAAoB,CAAC,SAAA,CAAU,UAAA,EAAY;AACzC,QAAA,SAAA,CAAU,UAAA,GAAc,CAAC,KAAA,EAAO,KAAA,EAAO,KAAK,CAAA,CAAY,QAAA,CAAS,KAAc,CAAA,GAAK,KAAA,GAAmC,KAAA;AAAA,MACzH;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,iBAAA,CAAkB,IAAS,IAAA,EAAuC;AACzE,EAAA,MAAM,QAAoB,EAAC;AAC3B,EAAA,MAAM,QAAoB,EAAC;AAC3B,EAAA,MAAM,YAA4B,EAAC;AACnC,EAAA,MAAM,UAAA,GAAa,IAAI,GAAA,CAAI,GAAA,CAAI,GAAG,QAAQ,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,GAAA,CAAI,CAAA,CAAE,EAAE,CAAC,CAAC,CAAA;AACjE,EAAA,KAAA,MAAW,CAAA,IAAK,GAAA,CAAI,EAAA,CAAG,QAAQ,CAAA,EAAG;AAChC,IAAA,MAAM,IAAA,GAAO,oBAAoB,CAAC,CAAA;AAClC,IAAA,KAAA,CAAM,KAAK,IAAI,CAAA;AACf,IAAA,KAAA,MAAW,CAAA,IAAK,IAAA,CAAK,CAAA,CAAE,aAAa,CAAA,EAAG;AACrC,MAAA,IAAI,MAAM,IAAA,CAAK,EAAA,IAAM,UAAA,CAAW,GAAA,CAAI,CAAC,CAAA,EAAG;AACtC,QAAA,KAAA,CAAM,IAAA,CAAK;AAAA,UACT,IAAA,EAAM,CAAA;AAAA,UACN,IAAI,IAAA,CAAK,EAAA;AAAA,UACT,IAAA,EAAM,WAAA;AAAA,UACN,MAAA,EAAQ,CAAA,sBAAA,EAAyB,CAAC,CAAA,2BAAA,EAA8B,KAAK,EAAE,CAAA;AAAA,SACxE,CAAA;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,EAAA,IAAI,GAAA,CAAI,EAAA,CAAG,aAAa,CAAA,CAAE,SAAS,CAAA,EAAG;AACpC,IAAA,MAAM,QAAA,uBAAe,GAAA,EAAoB;AACzC,IAAA,GAAA,CAAI,GAAG,aAAa,CAAA,CAAE,OAAA,CAAQ,CAAC,GAAG,GAAA,KAAQ;AACxC,MAAA,MAAM,GAAA,GAAM,GAAA,CAAI,CAAA,CAAE,UAAU,CAAA;AAC5B,MAAA,IAAI,GAAA,WAAc,GAAA,CAAI,GAAA,EAAK,IAAI,CAAA,CAAE,KAAA,EAAO,GAAA,GAAM,CAAC,CAAC,CAAA;AAAA,IAClD,CAAC,CAAA;AACD,IAAA,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAA,CAAO,SAAS,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA,IAAK,QAAQ,QAAA,CAAS,GAAA,CAAI,CAAA,CAAE,EAAE,KAAK,GAAA,CAAI,CAAA;AAAA,EAChF;AAEA,EAAA,IAAI,EAAA,GAAK,CAAA;AACT,EAAA,KAAA,MAAW,GAAA,IAAO,GAAA,CAAI,EAAA,CAAG,UAAU,CAAA,EAAG;AACpC,IAAA,EAAA,IAAM,CAAA;AACN,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,WAAW,EAAE,MAAA,CAAO,CAAC,EAAA,KAAO,KAAA,CAAM,KAAK,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,EAAE,CAAC,CAAA;AAC/E,IAAA,IAAI,GAAA,CAAI,SAAS,CAAA,EAAG;AAClB,MAAA,SAAA,CAAU,IAAA,CAAK;AAAA,QACb,EAAA,EAAI,GAAA,CAAI,GAAA,CAAI,EAAA,EAAI,QAAQ,EAAE,CAAA;AAAA,QAC1B,IAAA,EAAM,GAAA,CAAI,GAAA,CAAI,IAAA,EAAM,cAAc,EAAE,CAAA;AAAA,QACpC,QAAA,EAAU,GAAA;AAAA,QACV,SAAA,EAAW,GAAA,CAAI,GAAA,CAAI,WAAA,EAAa,EAAE;AAAA,OACnC,CAAA;AAAA,IACH;AAAA,EACF;AACA,EAAA,kBAAA,CAAmB,KAAK,CAAA;AACxB,EAAA,OAAO,sBAAA,CAAuB;AAAA,IAC5B,cAAA,EAAgB,CAAA;AAAA,IAChB,MAAA,EAAQ,EAAE,UAAA,EAAY,iBAAA,EAAmB,QAAA,EAAU,IAAA,CAAK,QAAA,IAAY,EAAC,EAAG,mBAAA,EAAqB,IAAA,CAAK,kBAAA,IAAsB,CAAA,EAAE;AAAA,IAC1H,cAAA,EAAgB,KAAA;AAAA,IAChB,gBAAA,EAAkB,KAAA;AAAA,IAClB,mBAAA,EAAqB,SAAA;AAAA,IACrB,QAAQ;AAAC,GACV,CAAA;AACH;AAEA,SAAS,eAAA,CAAgB,IAAS,IAAA,EAAuC;AACvE,EAAA,MAAM,QAAoB,EAAC;AAC3B,EAAA,MAAM,QAAoB,EAAC;AAC3B,EAAA,MAAM,YAA4B,EAAC;AACnC,EAAA,GAAA,CAAI,EAAA,CAAG,QAAQ,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAA,EAAG,EAAA,KAAO,gBAAgB,CAAA,EAAG,GAAA,CAAI,EAAE,EAAA,EAAI,GAAA,GAAM,KAAK,EAAA,GAAK,CAAC,CAAC,CAAA,EAAG,KAAA,EAAO,KAAA,EAAO,SAAS,CAAC,CAAA;AAC9G,EAAA,IAAI,KAAA,CAAM,WAAW,CAAA,IAAK,GAAA,CAAI,GAAG,QAAQ,CAAA,CAAE,SAAS,CAAA,EAAG;AACrD,IAAA,MAAM,QAAA,GAAW,GAAA,CAAK,EAAA,CAAG,cAAA,IAAkB,EAAU,CAAA,CAAE,MAAA,GAAS,CAAA,IAAK,GAAA,CAAK,EAAA,CAAG,cAAA,EAAoC,KAAK,EAAE,MAAA,GAAS,CAAA;AACjI,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,oBAAA,GAAuB,IAAI,EAAA,CAAG,QAAQ,EAAE,MAAA,GAAS,qFAAA,IAChD,WACG,oLAAA,GACA,mGAAA;AAAA,KACN;AAAA,EACF;AACA,EAAA,kBAAA,CAAmB,KAAK,CAAA;AACxB,EAAA,OAAO,sBAAA,CAAuB;AAAA,IAC5B,cAAA,EAAgB,CAAA;AAAA,IAChB,MAAA,EAAQ,EAAE,UAAA,EAAY,eAAA,EAAiB,QAAA,EAAU,IAAA,CAAK,QAAA,IAAY,EAAC,EAAG,mBAAA,EAAqB,IAAA,CAAK,kBAAA,IAAsB,CAAA,EAAE;AAAA,IACxH,cAAA,EAAgB,KAAA;AAAA,IAChB,gBAAA,EAAkB,KAAA;AAAA,IAClB,mBAAA,EAAqB,SAAA;AAAA,IACrB,QAAQ;AAAC,GACV,CAAA;AACH;AAGO,SAAS,kBAAA,CACd,KAAA,EACA,IAAA,GAAwB,EAAC,EACT;AAChB,EAAA,MAAM,CAAA,GAAI,KAAA;AACV,EAAA,IAAI,GAAA,CAAI,CAAA,CAAE,cAAc,CAAA,CAAE,SAAS,CAAA,EAAG;AACpC,IAAA,OAAO,uBAAuB,CAAC,CAAA;AAAA,EACjC;AACA,EAAA,IAAI,CAAA,CAAE,aAAA,IAAiB,CAAA,CAAE,eAAA,IAAmB,IAAI,CAAA,CAAE,KAAK,CAAA,CAAE,MAAA,GAAS,KAAK,GAAA,CAAI,CAAA,CAAE,MAAM,CAAA,CAAE,SAAS,CAAA,EAAG;AAC/F,IAAA,OAAO,cAAA,CAAe,GAAG,IAAI,CAAA;AAAA,EAC/B;AACA,EAAA,IAAI,GAAA,CAAI,CAAA,CAAE,QAAQ,CAAA,CAAE,SAAS,CAAA,IAAK,GAAA,CAAI,CAAA,CAAE,IAAI,CAAA,KAAM,iBAAA,EAAmB,OAAO,iBAAA,CAAkB,GAAG,IAAI,CAAA;AACrG,EAAA,OAAO,eAAA,CAAgB,GAAG,IAAI,CAAA;AAChC","file":"chunk-TWKIVUTQ.mjs","sourcesContent":["import {\n validateCurriculumFeed, type CurriculumFeed, type FeedNode, type FeedEdge, type FeedGrouping,\n} from '../schemas/curriculumFeedSchema';\n\n/**\n * CurriculumFeed emitter - pure transformation from a VERIFIED domain graph\n * to the planner-ready feed. No LLM calls: hints derive from existing\n * annotations only. Validates its own output (fail-closed).\n *\n * Phase-driven emission (hybrid graphs with development phases): the teaching\n * sequence IS the phase sequence. Concepts are emitted at the phase that\n * introduces them (aspect new) or revisits them at higher depth (aspect\n * advanced) - never as a trailing knowledge appendix. Concept minutes stay\n * UNMERGED: no prerequisite-minute inflation (the planner schedules the\n * revisit, not the total).\n */\n\nexport interface EmitFeedOptions {\n hallucinationCount?: number;\n warnings?: string[];\n}\n\nconst BLOOM_RANK: Record<string, number> = { Remember: 0, Understand: 1, Apply: 2, Analyze: 3, Evaluate: 4, Create: 5 };\nconst pad2 = (n: number): string => String(n).padStart(2, '0');\n\ntype Rec = Record<string, unknown>;\nconst arr = (v: unknown): Rec[] => (Array.isArray(v) ? (v as Rec[]) : []);\nconst str = (v: unknown, d = ''): string => (typeof v === 'string' ? v : d);\nconst num = (v: unknown, d: number): number => (typeof v === 'number' && Number.isFinite(v) ? v : d);\nconst strs = (v: unknown): string[] => (Array.isArray(v) ? v.filter((x): x is string => typeof x === 'string') : []);\n\nfunction stepMinutes(step: Rec): number {\n const effort = (step.effort ?? {}) as Rec;\n return Math.max(0, Math.round(num(effort.estimated_minutes, num(step.estimated_minutes, 45))));\n}\n\nfunction pushWarning(opts: EmitFeedOptions, message: string): void {\n (opts.warnings ?? (opts.warnings = [])).push(message);\n}\n\nfunction newKeywordsInOrder(nodes: FeedNode[]): void {\n // First appearance in emission order becomes 'new'; everything seen before is 'prerequisite'.\n const seen = new Set<string>();\n for (const n of nodes) {\n const all = n.keywords.all;\n n.keywords.new = [];\n n.keywords.prerequisite = [];\n for (const k of all) {\n if (seen.has(k)) n.keywords.prerequisite.push(k);\n else n.keywords.new.push(k);\n seen.add(k);\n }\n }\n}\n\nfunction feedNodeFromStep(step: Rec, featureId: string): FeedNode {\n const id = str(step.id);\n return {\n id,\n kind: 'product_step',\n name: str(step.name, id),\n concept_codes: strs(step.concept_codes),\n keywords: { all: strs(step.keywords), new: [], prerequisite: [] },\n estimated_minutes: stepMinutes(step),\n bloom_hint: 'Apply',\n depth_hint: null,\n completion_level: 'standard',\n scaffolding: { scaffold_candidates: [], core_value_minutes: stepMinutes(step) },\n references: arr(step.files).map((f) => ({ file: str(f), evidence: 'step file' })).filter((f) => f.file),\n feature_id: featureId,\n concept_ref: null,\n // P52/T2.1: carry the step's tangible outcome so the planner can cut\n // sessions at user-visible checkpoints instead of raw minute counts.\n user_visible_deliverable: str((step.outcome as Rec | undefined)?.user_visible),\n is_core: false,\n phase_id: '',\n introduce_aspect: null,\n };\n}\n\nfunction deriveConceptDepthHint(concept: Rec): FeedNode['depth_hint'] {\n const explicit = str(concept.depth_hint);\n if (explicit === 'ulo' || explicit === 'cio' || explicit === 'sio') return explicit;\n\n const sio = arr(concept.sio);\n const prereqs = strs(concept.prerequisites);\n const pts = arr(concept.problem_types);\n const hasHighBloom = pts.some((p) => {\n const b = str(p.bloom_level);\n return b === 'Apply' || b === 'Analyze' || b === 'Evaluate' || b === 'Create';\n });\n\n if (sio.length >= 2 || hasHighBloom) return 'sio';\n if (str(concept.cio) || prereqs.length >= 1) return 'cio';\n return 'ulo';\n}\n\n/**\n * P52/T3.1: minute estimates per depth level for one concept occurrence.\n * ULO (know-it-exists) is a fraction of the full teach; SIO (implementation)\n * equals the feed's estimated minutes. Only DOWNWARD candidates are offered\n * and the ULO floor is never crossed (planner filters is_core itself).\n */\nfunction deriveDepthInputs(minutes: number, depth: FeedNode['depth_hint']): Pick<FeedNode, 'depth_variants' | 'depth_scaffold_candidates'> {\n const ulo = Math.max(5, Math.round(minutes * 0.35));\n const cio = Math.max(ulo + 5, Math.round(minutes * 0.65));\n const sio = Math.max(cio + 5, minutes);\n const variants = { ulo, cio, sio };\n const candidates: NonNullable<FeedNode['depth_scaffold_candidates']> = [];\n if (depth === 'sio') {\n candidates.push({ from_depth: 'sio', to_depth: 'cio', minutes_saved: sio - cio, reason: 'SIO implementation practice deferred — CIO mechanism suffices for downstream consumers' });\n }\n if (depth === 'cio' || depth === 'sio') {\n candidates.push({ from_depth: depth, to_depth: 'ulo', minutes_saved: (depth === 'sio' ? sio : cio) - ulo, reason: 'Mechanism deferred — ULO awareness suffices (know-it-exists); never below the ULO floor' });\n }\n return { depth_variants: variants, depth_scaffold_candidates: candidates };\n}\n\nfunction feedNodeFromConcept(concept: Rec): FeedNode {\n const id = str(concept.id);\n const pts = arr(concept.problem_types)\n .map((p) => str(p.bloom_level, 'Understand'))\n .filter((b) => b in BLOOM_RANK)\n .sort((a, b) => BLOOM_RANK[a] - BLOOM_RANK[b]);\n const topBloom = pts[pts.length - 1] ?? 'Understand';\n\n const rawRefs = arr(concept.references);\n const references = rawRefs.length > 0\n ? rawRefs.map((r) => ({ file: str(r.file, 'syllabus.md'), evidence: str(r.evidence, str(concept.name)) })).filter((r) => r.file)\n : [{ file: 'syllabus.md', evidence: str(concept.category ? `Topic under category ${str(concept.category)}` : str(concept.name), id) }];\n\n const problemScaffolds = arr(concept.problem_types).map((pt) => ({\n item: str(pt.name, 'Problem type'),\n reason: str(pt.description, 'Formative practice check'),\n minutes_saved: num(pt.estimated_minutes, 10),\n suggest: 'provide_partial' as const,\n }));\n\n return {\n id,\n kind: 'concept',\n name: str(concept.name, id),\n concept_codes: [str(concept.code) || id].filter(Boolean),\n keywords: { all: strs(concept.keywords), new: [], prerequisite: [] },\n estimated_minutes: Math.max(0, Math.round(num(concept.estimated_minutes, 15))),\n bloom_hint: topBloom as FeedNode['bloom_hint'],\n depth_hint: deriveConceptDepthHint(concept),\n completion_level: 'standard',\n scaffolding: {\n scaffold_candidates: problemScaffolds,\n core_value_minutes: Math.max(0, Math.round(num(concept.estimated_minutes, 15))),\n },\n references,\n feature_id: null,\n concept_ref: id,\n user_visible_deliverable: '', // concepts are taught, not shipped — checkpoints live on product steps\n ...deriveDepthInputs(Math.max(0, Math.round(num(concept.estimated_minutes, 15))), deriveConceptDepthHint(concept)),\n is_core: false,\n depth_context: {\n ulo: str(concept.ulo),\n cio: str(concept.cio),\n sio: strs(concept.sio),\n },\n phase_id: '',\n introduce_aspect: null,\n };\n}\n\nfunction addFeatureGroup(\n f: Rec, fid: string, nodes: FeedNode[], edges: FeedEdge[], groupings: FeedGrouping[],\n): string[] {\n const steps = arr(f.steps);\n const stepIds: string[] = [];\n let prev = '';\n for (let i = 0; i < steps.length; i++) {\n const stepNode = feedNodeFromStep(steps[i], fid);\n if (!stepNode.id) continue;\n stepIds.push(stepNode.id);\n nodes.push(stepNode);\n if (prev) edges.push({ from: prev, to: stepNode.id, kind: 'task', reason: 'feature build order' });\n prev = stepNode.id;\n }\n for (const dep of strs(f.depends_on)) {\n const depFirst = nodes.find((n) => n.feature_id === dep);\n if (depFirst && stepIds[0]) edges.push({ from: depFirst.id, to: stepIds[0], kind: 'task', reason: 'feature depends_on ' + dep });\n }\n if (stepIds.length > 0) groupings.push({ id: 'G-' + fid, name: str(f.name, fid), node_ids: stepIds, rationale: str(f.description, '') });\n return stepIds;\n}\n\ninterface EmitPhaseCtx {\n id: string;\n order: number;\n name: string;\n productCompletion: string;\n conceptOrder: string[]; // concept node ids in teaching order (new first, then advanced)\n featureIds: string[];\n}\n\n/**\n * Phase-driven hybrid emission:\n * - emit concepts per phase (aspect new first, then advanced) BEFORE the\n * phase's features; knowledge edges concept -> first step of consumer\n * features carry the JIT relation\n * - feature order inside a phase follows the project graph order\n * - phases and their node_ids go into feed.phases (teaching sequence)\n * - concept minutes stay UNMERGED (no prereq-minute accumulation)\n */\nfunction emitFromHybrid(hybrid: Rec, opts: EmitFeedOptions): CurriculumFeed {\n const nodes: FeedNode[] = [];\n const edges: FeedEdge[] = [];\n const groupings: FeedGrouping[] = [];\n const pg = (hybrid.project_graph ?? {}) as Rec;\n const kg = (hybrid.knowledge_graph ?? {}) as Rec;\n const rawPhases = arr(hybrid.phases).filter((p) => str(p.id));\n\n const features = arr(pg.features);\n const conceptRecs = arr(kg.concepts);\n const conceptById = new Map(conceptRecs.map((c) => [str(c.id), c]));\n\n // --- no phases -> legacy path (concepts first, then features) -----------\n if (rawPhases.length === 0) {\n for (const c of conceptRecs) {\n const node = feedNodeFromConcept(c);\n nodes.push(node);\n for (const p of strs(c.prerequisites)) edges.push({ from: p, to: node.id, kind: 'knowledge', reason: 'concept prerequisite' });\n }\n features.forEach((f, fi) => addFeatureGroup(f, str(f.id, 'F' + pad2(fi + 1)), nodes, edges, groupings));\n linkHybridKnowledgeEdges(hybrid, nodes, edges, true);\n newKeywordsInOrder(nodes);\n return validateCurriculumFeed({\n schema_version: 2,\n source: { graph_type: 'hybrid_graph', warnings: opts.warnings ?? [], hallucination_count: opts.hallucinationCount ?? 0 },\n learning_nodes: nodes,\n dependency_edges: edges,\n suggested_groupings: groupings,\n phases: [],\n });\n }\n\n // --- phase-driven emission ----------------------------------------------\n const featureOrder = new Map<string, number>();\n features.forEach((f, fi) => featureOrder.set(str(f.id, 'F' + pad2(fi + 1)), fi));\n const stepIdsByFeature = new Map<string, string[]>();\n for (const f of features) {\n const fid = str(f.id);\n stepIdsByFeature.set(fid, arr(f.steps).map((s) => str(s.id)).filter(Boolean));\n }\n\n const emitCtx: EmitPhaseCtx[] = [];\n const emittedConcepts = new Set<string>();\n const previewsEmitted = new Set<string>();\n const linkList = arr(hybrid.links);\n\n for (const rp of rawPhases) {\n const pid = str(rp.id);\n const order = num(rp.order, emitCtx.length + 1);\n const conceptOrder: string[] = [];\n const intros = arr(rp.introduces);\n // new introductions first, then advanced revisits, then previews\n // (awareness passes — never the introduction itself)\n const news = intros.filter((i) => str(i.aspect, 'new') === 'new');\n const advanced = intros.filter((i) => str(i.aspect) === 'advanced');\n const previews = intros.filter((i) => str(i.aspect) === 'preview');\n for (const intro of [...news, ...advanced, ...previews]) {\n const cid = str(intro.concept_id);\n const c = conceptById.get(cid);\n if (!c) {\n throw new Error('Phase ' + pid + ' introduces unknown concept ' + cid + ' - fail-closed');\n }\n if (str(intro.aspect, 'new') === 'advanced' && !emittedConcepts.has(cid)) {\n throw new Error('Phase ' + pid + ' advanced-revisits concept ' + cid + ' that was never emitted - fail-closed');\n }\n if (str(intro.aspect, 'new') === 'preview') {\n if (emittedConcepts.has(cid)) {\n throw new Error('Phase ' + pid + ' previews concept ' + cid + ' that was already introduced - preview must precede the anchor teach - fail-closed');\n }\n if (previewsEmitted.has(cid)) {\n throw new Error('Phase ' + pid + ' duplicate preview of concept ' + cid + ' - fail-closed');\n }\n const pidIdx = rawPhases.findIndex((rp2) => str(rp2.id) === pid);\n const anchorIdx = rawPhases.findIndex((rp2) => arr(rp2.introduces).some((i2) => str(i2.concept_id) === cid && str(i2.aspect, 'new') !== 'preview'));\n if (anchorIdx < 0) {\n throw new Error('Phase ' + pid + ' previews concept ' + cid + ' that is never fully introduced in any phase - fail-closed');\n }\n if (pidIdx >= anchorIdx) {\n throw new Error('Phase ' + pid + ' previews concept ' + cid + ' whose anchor introduction is not in a LATER phase - fail-closed');\n }\n previewsEmitted.add(cid);\n // ULO awareness node: \"know it exists\". Keeps the concept vocabulary\n // (keywords.all) so the emitter's in-order pass makes the PREVIEW the\n // legitimate introducer of those words — a product step must never be\n // the introducer (the overtake guard's whole point). Depth pinned to\n // ulo, bloom to Understand, ~25% minutes, no prerequisite edges, no\n // scaffolding (nothing to practice yet).\n const prevNode = feedNodeFromConcept(c);\n prevNode.id = cid + '__PREV' + pad2(order);\n prevNode.name = str(c.name, cid) + ' (preview)';\n prevNode.phase_id = pid;\n prevNode.introduce_aspect = 'preview';\n prevNode.estimated_minutes = Math.max(5, Math.round(prevNode.estimated_minutes * 0.25));\n prevNode.depth_hint = 'ulo';\n prevNode.bloom_hint = 'Understand';\n prevNode.concept_codes = [str(c.code) || cid];\n prevNode.keywords = { all: strs(c.keywords), new: [], prerequisite: [] };\n prevNode.scaffolding = { scaffold_candidates: [], core_value_minutes: 0 };\n prevNode.depth_variants = { ulo: prevNode.estimated_minutes, cio: prevNode.estimated_minutes, sio: prevNode.estimated_minutes };\n prevNode.depth_scaffold_candidates = []; // nothing to downgrade — it IS the floor\n nodes.push(prevNode);\n conceptOrder.push(prevNode.id);\n continue;\n }\n if (!emittedConcepts.has(cid)) {\n const node = feedNodeFromConcept(c);\n node.phase_id = pid;\n node.introduce_aspect = 'new';\n nodes.push(node);\n for (const p of strs(c.prerequisites)) {\n if (!emittedConcepts.has(p)) {\n throw new Error('Concept ' + cid + ' (phase ' + pid + ') has prerequisite ' + p + ' that is not introduced in an earlier phase - fail-closed');\n }\n edges.push({ from: p, to: cid, kind: 'knowledge', reason: 'concept prerequisite' });\n }\n emittedConcepts.add(cid);\n conceptOrder.push(cid);\n } else {\n // advanced revisit: re-emit as a NEW node with a revisit id so the\n // planner can schedule the higher-depth pass without double-counting\n // the first introduction\n const revisitId = cid + '__ADV' + pad2(order);\n const node = feedNodeFromConcept(c);\n node.id = revisitId;\n node.name = str(c.name, cid) + ' (advanced)';\n node.phase_id = pid;\n node.introduce_aspect = 'advanced';\n node.depth_scaffold_candidates = [{ from_depth: 'sio', to_depth: 'cio', minutes_saved: Math.round(node.estimated_minutes * 0.3), reason: 'advanced revisit may drop to CIO recap without breaking first-teach chain' }];\n node.estimated_minutes = Math.max(15, Math.round(node.estimated_minutes * 0.5));\n node.concept_codes = [str(c.code) || cid];\n edges.push({ from: cid, to: revisitId, kind: 'knowledge', reason: 'advanced revisit of ' + cid + ' at phase ' + pid });\n nodes.push(node);\n conceptOrder.push(revisitId);\n }\n }\n const featureIds = strs(rp.feature_ids)\n .filter((fid) => featureOrder.has(fid))\n .sort((a, b) => featureOrder.get(a)! - featureOrder.get(b)!);\n emitCtx.push({ id: pid, order, name: str(rp.name, pid), productCompletion: str(rp.product_completion), conceptOrder, featureIds });\n }\n\n // Fail-closed: every concept consumed by a link must be emitted somewhere\n for (const l of linkList) {\n const cid = str(l.concept_id);\n if (cid && conceptById.has(cid) && !emittedConcepts.has(cid)) {\n throw new Error('Concept ' + cid + ' is consumed by links but never introduced in any phase - fail-closed');\n }\n }\n // Fail-closed: every feature must sit in exactly one emitted phase\n const seenFeatures = new Set(emitCtx.flatMap((e) => e.featureIds));\n const missingFeatures = features.map((f) => str(f.id)).filter((fid) => fid && !seenFeatures.has(fid));\n if (missingFeatures.length > 0) {\n throw new Error('Features not covered by any phase: ' + missingFeatures.join(', ') + ' - fail-closed');\n }\n\n // Emit feature steps phase by phase (teaching sequence)\n const phaseNodeIds: string[][] = [];\n for (const ctx of emitCtx) {\n const ids: string[] = [...ctx.conceptOrder];\n for (const fid of ctx.featureIds) {\n const f = features.find((x) => str(x.id) === fid)!;\n const stepIds = addFeatureGroup(f, fid, nodes, edges, groupings);\n for (const sid of stepIds) {\n const node = nodes.find((n) => n.id === sid)!;\n node.phase_id = ctx.id;\n }\n ids.push(...stepIds);\n }\n phaseNodeIds.push(ids);\n }\n\n // Teaching-order emission (2026-09-17 incident): the nodes array MUST\n // follow the phase sequence. Downstream consumers (planner unit grouping,\n // keyword new/prerequisite labeling, keyword-edge materialization) treat\n // array order as teaching order; emitting \"all concepts first, steps\n // after\" diverged from the phase order and manufactured backwards\n // prerequisite edges across phases (e.g. C8 in PH3 labeled before a PH2\n // step that consumes its keyword) — a self-contradictory feed.\n const placed = new Set<string>(phaseNodeIds.flat());\n const orderedNodes: FeedNode[] = [];\n for (const ids of phaseNodeIds) {\n for (const id of ids) {\n const n = nodes.find((x) => x.id === id);\n if (n) orderedNodes.push(n);\n }\n }\n for (const n of nodes) if (!placed.has(n.id)) orderedNodes.push(n);\n nodes.length = 0;\n nodes.push(...orderedNodes);\n\n // Knowledge edges: concept -> first step of each consumer feature (JIT).\n // Deduped by (from,to): several links may target the same feature.\n const edgeSeen = new Set(edges.map((e) => e.from + '>' + e.to));\n const pushEdge = (from: string, to: string, reason: string) => {\n const key = from + '>' + to;\n if (edgeSeen.has(key)) return;\n edgeSeen.add(key);\n edges.push({ from, to, kind: 'knowledge', reason });\n };\n for (const l of linkList) {\n const cid = str(l.concept_id);\n const fid = str(l.feature_id);\n if (!conceptById.has(cid)) continue;\n const firstStep = (stepIdsByFeature.get(fid) || [])[0];\n if (!firstStep) continue;\n const depth = str(l.depth, 'cio');\n // Preview-aware guard: when the concept's anchor teach sits in a LATER\n // phase than the consumer step, the consumer is served by its __PREV\n // awareness node — the anchor edge would cross phases backwards. Never\n // manufacture it; record the drift instead.\n const anchorNode = nodes.find((n2) => n2.kind === 'concept' && n2.concept_ref === cid && n2.introduce_aspect === 'new');\n const stepNode = nodes.find((n2) => n2.id === firstStep);\n const anchorPhaseIdx = anchorNode ? emitCtx.findIndex((c2) => c2.id === anchorNode.phase_id) : -1;\n const stepPhaseIdx = stepNode ? emitCtx.findIndex((c2) => c2.id === stepNode.phase_id) : -1;\n if (emitCtx.length > 0 && anchorPhaseIdx > stepPhaseIdx) {\n pushWarning(opts, 'hybrid link drift: concept ' + cid + ' is fully taught in a later phase than consumer ' + firstStep + ' (a preview covers the dependency)');\n continue;\n }\n pushEdge(cid, firstStep, str(l.relationship, 'hybrid link') + ' [' + depth + ']');\n // Advanced revisits feed only features of the revisit's OWN phase: a PH2\n // revisit must never become a prerequisite of a PH1 feature (teaching\n // order is phase-ordered).\n for (const n of nodes) {\n if (n.kind === 'concept' && n.concept_ref === cid && n.introduce_aspect === 'advanced' && n.phase_id === str(l.phase_id)) {\n pushEdge(n.id, firstStep, 'advanced application [' + depth + ']');\n }\n }\n }\n\n newKeywordsInOrder(nodes);\n\n // Deterministic keyword-prerequisite materialization: when a node declares a\n // keyword as PREREQUISITE, some other node must INTRODUCE it (new) EARLIER in\n // feed order. Missing edge = planner can schedule the consumer first while the\n // audit flags ordering it cannot fix. Edge kind 'keyword' — the planner\n // honors it for topological order; 'mastered_before_course' entries are exempt\n // (entry-level keywords need no introducer).\n const feedOrder = new Map(nodes.map((n, i) => [n.id, i]));\n const introducer = new Map<string, string>();\n for (const n of nodes) for (const k of n.keywords?.new ?? []) {\n const key = k.toLowerCase();\n if (!introducer.has(key)) introducer.set(key, n.id);\n }\n for (const n of nodes) {\n for (const k of n.keywords?.prerequisite ?? []) {\n const src = introducer.get(k.toLowerCase());\n if (!src || src === n.id) continue;\n const srcNode = nodes.find((x) => x.id === src);\n // Phase-aware guard: when phases define teaching order, a keyword edge\n // must never cross phases backwards, and a CONCEPT must never be\n // \"introduced\" by a product STEP that happens to run earlier (the\n // graph anchored the concept downstream of its consumer — teaching\n // order would then declare the step as the origin of foundational\n // knowledge). Do NOT manufacture such edges: record the drift for the\n // host to repair (move the introducer earlier) and let\n // validateCurriculumFeed's teaching-order invariant be the hard gate.\n const phaseOf = (id: string): number => {\n const n2 = nodes.find((x) => x.id === id);\n return n2 ? emitCtx.findIndex((c) => c.id === n2.phase_id) : -1;\n };\n const conceptOvertaken = n.kind === 'concept' && srcNode?.kind === 'product_step';\n const fromPhase = phaseOf(src);\n const toPhase = phaseOf(n.id);\n if (conceptOvertaken || (emitCtx.length > 0 && fromPhase > toPhase)) {\n pushWarning(opts,\n 'keyword-order drift: introducer ' + src + ' sits in a later phase than consumer ' + n.id +\n ' (keyword \"' + k + '\") — host should move the introducer earlier',\n );\n continue;\n }\n if (feedOrder.get(src)! < feedOrder.get(n.id)!) pushEdge(src, n.id, 'keyword prerequisite [' + k + ']');\n }\n }\n\n const phases = emitCtx.map((ctx, i) => ({\n id: ctx.id,\n order: ctx.order,\n name: ctx.name,\n product_completion: ctx.productCompletion || 'Phase ' + (i + 1) + ' deliverable',\n node_ids: phaseNodeIds[i],\n }));\n\n return validateCurriculumFeed({\n schema_version: 2,\n source: { graph_type: 'hybrid_graph', warnings: opts.warnings ?? [], hallucination_count: opts.hallucinationCount ?? 0 },\n learning_nodes: nodes,\n dependency_edges: edges,\n suggested_groupings: groupings,\n phases,\n });\n}\n\n/** Legacy helper: knowledge edges from hybrid links (used by the no-phase path). */\nfunction linkHybridKnowledgeEdges(hybrid: Rec, nodes: FeedNode[], edges: FeedEdge[], setStepHints: boolean): void {\n const conceptIds = new Set(nodes.filter((n) => n.kind === 'concept').map((n) => n.concept_ref || n.id));\n for (const l of arr(hybrid.links)) {\n const conceptId = str(l.concept_id);\n const featureId = str(l.feature_id);\n if (!conceptIds.has(conceptId)) continue;\n const depth = str(l.depth, 'cio');\n const firstStep = nodes.find((n) => n.feature_id === featureId);\n if (firstStep) {\n edges.push({ from: conceptId, to: firstStep.id, kind: 'knowledge', reason: str(l.relationship, 'hybrid link') + ' [' + depth + ']' });\n if (setStepHints && !firstStep.depth_hint) {\n firstStep.depth_hint = (['ulo', 'cio', 'sio'] as const).includes(depth as 'ulo') ? (depth as FeedNode['depth_hint']) : 'cio';\n }\n }\n }\n}\n\nfunction emitFromKnowledge(kg: Rec, opts: EmitFeedOptions): CurriculumFeed {\n const nodes: FeedNode[] = [];\n const edges: FeedEdge[] = [];\n const groupings: FeedGrouping[] = [];\n const conceptIds = new Set(arr(kg.concepts).map((c) => str(c.id)));\n for (const c of arr(kg.concepts)) {\n const node = feedNodeFromConcept(c);\n nodes.push(node);\n for (const p of strs(c.prerequisites)) {\n if (p !== node.id && conceptIds.has(p)) {\n edges.push({\n from: p,\n to: node.id,\n kind: 'knowledge',\n reason: `concept prerequisite: ${p} must be understood before ${node.id}`,\n });\n }\n }\n }\n\n // Sort nodes by learning_path order if present\n if (arr(kg.learning_path).length > 0) {\n const orderMap = new Map<string, number>();\n arr(kg.learning_path).forEach((s, idx) => {\n const cid = str(s.concept_id);\n if (cid) orderMap.set(cid, num(s.order, idx + 1));\n });\n nodes.sort((a, b) => (orderMap.get(a.id) ?? 999) - (orderMap.get(b.id) ?? 999));\n }\n\n let ci = 0;\n for (const cat of arr(kg.categories)) {\n ci += 1;\n const ids = strs(cat.concept_ids).filter((id) => nodes.some((n) => n.id === id));\n if (ids.length > 0) {\n groupings.push({\n id: str(cat.id, 'cat' + ci),\n name: str(cat.name, 'Category ' + ci),\n node_ids: ids,\n rationale: str(cat.description, ''),\n });\n }\n }\n newKeywordsInOrder(nodes);\n return validateCurriculumFeed({\n schema_version: 2,\n source: { graph_type: 'knowledge_graph', warnings: opts.warnings ?? [], hallucination_count: opts.hallucinationCount ?? 0 },\n learning_nodes: nodes,\n dependency_edges: edges,\n suggested_groupings: groupings,\n phases: [],\n });\n}\n\nfunction emitFromProject(pg: Rec, opts: EmitFeedOptions): CurriculumFeed {\n const nodes: FeedNode[] = [];\n const edges: FeedEdge[] = [];\n const groupings: FeedGrouping[] = [];\n arr(pg.features).forEach((f, fi) => addFeatureGroup(f, str(f.id, 'F' + pad2(fi + 1)), nodes, edges, groupings));\n if (nodes.length === 0 && arr(pg.features).length > 0) {\n const hasTasks = arr((pg.implementation ?? {}) as Rec).length > 0 || arr((pg.implementation as Rec | undefined)?.tasks).length > 0;\n throw new Error(\n 'Project graph has ' + arr(pg.features).length + ' features but NO features[].steps - the curriculum feed cannot emit planning nodes.' +\n (hasTasks\n ? ' This looks like a legacy knowledge-tree graph (implementation.tasks). Regenerate it with the domain-kit v3 pipeline (runProjectGraphPipeline / hybridGraphPipeline), then re-run.'\n : ' Every feature must carry at least one step with id/name/keywords/effort for curriculum planning.'),\n );\n }\n newKeywordsInOrder(nodes);\n return validateCurriculumFeed({\n schema_version: 2,\n source: { graph_type: 'project_graph', warnings: opts.warnings ?? [], hallucination_count: opts.hallucinationCount ?? 0 },\n learning_nodes: nodes,\n dependency_edges: edges,\n suggested_groupings: groupings,\n phases: [],\n });\n}\n\n/** Emit a CurriculumFeed from whichever graph shape is provided (already verified upstream). */\nexport function emitCurriculumFeed(\n graph: unknown,\n opts: EmitFeedOptions = {},\n): CurriculumFeed {\n const g = graph as Rec;\n if (arr(g.learning_nodes).length > 0) {\n return validateCurriculumFeed(g); // already a feed - pass-through validation\n }\n if (g.project_graph || g.knowledge_graph || arr(g.links).length > 0 || arr(g.phases).length > 0) {\n return emitFromHybrid(g, opts);\n }\n if (arr(g.concepts).length > 0 || str(g.type) === 'knowledge_graph') return emitFromKnowledge(g, opts);\n return emitFromProject(g, opts);\n}\n"]}
@@ -1778,7 +1778,42 @@ async function generateKnowledgeGraph(options) {
1778
1778
  const structuredTopicsGuide = parsedSyllabus && parsedSyllabus.allTopics.length > 0 ? `
1779
1779
  STRUCTURED SYLLABUS TOPICS (Every topic MUST map to at least one concept to ensure 100% syllabus coverage):
1780
1780
  ` + parsedSyllabus.allTopics.map((t) => `- [${t.unitTitle}] ${t.id}: ${t.title} (Keywords: ${t.keywords.join(", ")})`).join("\n") + "\n" : "";
1781
- const systemPrompt = "You are an expert curriculum designer. Decompose the given subject into a structured knowledge graph with concepts, prerequisites, and problem types.\n\nCRITICAL RULES:\n1. Each concept MUST have the three depth layers, authored against this CONTRACT:\n - ulo (WHAT + WHY): bound to the CONCEPT's intrinsic NATURE. It explains what the\n concept is and why it exists \u2014 the problem it solves in principle. It must be\n TECHNOLOGY-AGNOSTIC: never name a concrete library, API, function, file format,\n tool or language feature. Typical assessment level: Remember / Understand.\n - cio (HOW): the MECHANISM \u2014 how the concept works and solves that problem, step\n by step. Still bound to the concept's nature: more concrete than the ULO, but\n NOT tied to one specific technology (no concrete API names). CIO capability is\n assessed INDIRECTLY, through completing contextual work, not in isolation.\n - sio (IMPLEMENTATION): an array of 2-4 items. EVERY item MUST reference concrete\n KEYWORDS/technologies (a real API, function, module, file, or named pattern) and\n describe a completable, context-specific implementation task. Application,\n Analysis and Creation skills are assessed INDIRECTLY through completing these\n items. There is NO fixed Bloom ceiling for a layer \u2014 but concreteness MUST\n strictly increase: SIO more technology-bound than CIO, CIO more concrete than ULO.\n - prerequisites: concept IDs that must be learned first (MUST NOT create cycles)\n - problem_types: 2-4 problem types with bloom_level and difficulty. Remember and\n Understand problem types test the ULO/CIO directly; Apply-and-above types are\n proxies for SIO completion in context.\n - techniques: methods, formulas, tools\n2. Prerequisite chains MUST be acyclic (no concept can depend on itself or create a loop)\n3. Concepts should be ordered by prerequisite dependency (topological sort)\n4. Problem types should range from Remember to Apply (not all at same level)\n5. Group concepts into categories (chapters/sections)\n6. ALL text in ENGLISH (even if input is Vietnamese)\n7. NEVER invent concepts not present in the syllabus\n8. Return JSON matching the KnowledgeGraphSchema";
1781
+ const systemPrompt = `You are an expert curriculum designer. Decompose the given subject into a structured knowledge graph with concepts, prerequisites, and problem types.
1782
+
1783
+ CRITICAL RULES:
1784
+ 1. Each concept MUST have the three depth layers, authored against this CONTRACT:
1785
+ - ulo (WHAT + WHY): bound to the CONCEPT's intrinsic NATURE. It explains what the
1786
+ concept is and why it exists \u2014 the problem it solves in principle. It must be
1787
+ TECHNOLOGY-AGNOSTIC: never name a concrete library, API, function, file format,
1788
+ tool or language feature. Typical assessment level: Remember / Understand.
1789
+ - cio (HOW): the MECHANISM \u2014 how the concept works and solves that problem, step
1790
+ by step. Still bound to the concept's nature: more concrete than the ULO, but
1791
+ NOT tied to one specific technology (no concrete API names). CIO capability is
1792
+ assessed INDIRECTLY, through completing contextual work, not in isolation.
1793
+ - sio (IMPLEMENTATION): an array of 2-4 items. EVERY item MUST reference concrete
1794
+ KEYWORDS/technologies (a real API, function, module, file, or named pattern) and
1795
+ describe a completable, context-specific implementation task. Application,
1796
+ Analysis and Creation skills are assessed INDIRECTLY through completing these
1797
+ items. There is NO fixed Bloom ceiling for a layer \u2014 but concreteness MUST
1798
+ strictly increase: SIO more technology-bound than CIO, CIO more concrete than ULO.
1799
+ - prerequisites: concept IDs that must be learned first (MUST NOT create cycles)
1800
+ - problem_types: 2-4 problem types with bloom_level and difficulty. Remember and
1801
+ Understand problem types test the ULO/CIO directly; Apply-and-above types are
1802
+ proxies for SIO completion in context.
1803
+ - techniques: methods, formulas, tools
1804
+ 2. Prerequisite chains MUST be acyclic (no concept can depend on itself or create a loop)
1805
+ 3. Concepts should be ordered by prerequisite dependency (topological sort)
1806
+ 4. Problem types should range from Remember to Apply (not all at same level)
1807
+ 5. Group concepts into categories (chapters/sections)
1808
+ 6. ALL text in ENGLISH (even if input is Vietnamese)
1809
+ 7. NEVER invent concepts not present in the syllabus
1810
+ 8. KEYWORDS: Each concept MUST include 5-10 domain-specific keywords that:
1811
+ - Are concrete terms a student encounters when studying THIS concept
1812
+ - Include at least 2 technology-specific terms (APIs, functions, types, tools)
1813
+ - Are DISTINCT from keywords of other concepts (minimal overlap)
1814
+ - NEVER generic English words (e.g. "view", "data", "code" are TOO generic)
1815
+ - Example for "SwiftUI Layout": ["VStack", "HStack", "ZStack", ".padding()", ".frame()", "alignment", "Spacer", "LazyVGrid"]
1816
+ 9. Return JSON matching the KnowledgeGraphSchema`;
1782
1817
  const userPrompt = `Subject: ${subject}
1783
1818
  ${description ? `Description: ${description}` : ""}
1784
1819
  ${structuredTopicsGuide}${truncatedSyllabus ? `Syllabus:
@@ -1799,8 +1834,9 @@ Return JSON:
1799
1834
  {
1800
1835
  "id": "C1",
1801
1836
  "name": "Concept name",
1802
- "description": "Brief description",
1837
+ "description": "2-3 sentence description: what this concept covers, why a learner encounters it, and what mastering it enables. Must be specific enough that a student reading only this description understands the concept's scope.",
1803
1838
  "category": "category_id",
1839
+ "keywords": ["specific_term_1", "concrete_api_2", "method_call_3", "tool_or_type_4", "syntax_keyword_5", "concept_term_6"],
1804
1840
  "ulo": "WHAT + WHY in technology-agnostic language: what the concept is and the problem it exists to solve. No library/API/tool names.",
1805
1841
  "cio": "HOW the mechanism works, still technology-agnostic but more concrete than the ULO.",
1806
1842
  "sio": ["Concrete task naming a REAL API/function/module/file from the target technology, completable end to end", "Another technology-bound task using a different concrete keyword"],
@@ -1841,18 +1877,23 @@ Return JSON:
1841
1877
  log("STEP_2", "Validating and cleaning knowledge graph...");
1842
1878
  const concepts = result.concepts || [];
1843
1879
  const categories = result.categories || [];
1880
+ for (const c of concepts) {
1881
+ if ((c.description || "").length < 50) {
1882
+ warnings.push("Concept " + c.id + ": description too short (" + (c.description || "").length + " chars) \u2014 may produce shallow KX narratives");
1883
+ }
1884
+ }
1844
1885
  {
1845
1886
  const STOP = /* @__PURE__ */ new Set(["and", "the", "for", "with", "from", "that", "this", "into", "when", "while", "using", "their", "your", "each", "how", "why", "what", "between", "through", "about"]);
1846
1887
  const tokensFrom = (text) => text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w) => w.length >= 4 && !STOP.has(w));
1847
1888
  for (const c of concepts) {
1848
1889
  const existing = (c.keywords || []).map((k) => String(k).trim()).filter(Boolean);
1849
1890
  if (existing.length === 0) {
1850
- const derived = [.../* @__PURE__ */ new Set([...tokensFrom(c.name), ...tokensFrom(c.description || "")])].slice(0, 5);
1891
+ const derived = [.../* @__PURE__ */ new Set([...tokensFrom(c.name), ...tokensFrom(c.description || "")])].slice(0, 8);
1851
1892
  if (derived.length === 0) {
1852
1893
  throw new Error("Concept " + c.id + " (" + c.name + ") has no keywords and none could be derived from name/description \u2014 fail-closed");
1853
1894
  }
1854
1895
  c.keywords = derived;
1855
- warnings.push("Concept " + c.id + ": empty keyword set - derived " + derived.join(", ") + " from name/description");
1896
+ warnings.push("Concept " + c.id + ": empty keyword set - derived " + derived.join(", ") + " from name/description (WARNING: LLM returned no keywords)");
1856
1897
  } else {
1857
1898
  c.keywords = existing;
1858
1899
  }
@@ -1977,6 +2018,52 @@ Return JSON:
1977
2018
  }
1978
2019
  log("STEP_5", "Depth layers authored: " + concepts.length + " concepts, audit " + lo.audits.filter((a) => a.passed).length + "/" + concepts.length + " passed");
1979
2020
  }
2021
+ {
2022
+ for (const concept of concepts) {
2023
+ const sioText = (concept.sio || []).join(" ");
2024
+ const techTokens = [];
2025
+ let m;
2026
+ const techTokenRe = /`([^`]+)`/g;
2027
+ while ((m = techTokenRe.exec(sioText)) !== null) {
2028
+ if (m[1].length >= 3 && m[1].length <= 40) techTokens.push(m[1]);
2029
+ }
2030
+ const annotationRe = /(?:^|[\s(])(@[A-Za-z]\w*)/g;
2031
+ while ((m = annotationRe.exec(sioText)) !== null) techTokens.push(m[1]);
2032
+ const dotRe = /\b\w+\.\w+\(/g;
2033
+ while ((m = dotRe.exec(sioText)) !== null) techTokens.push(m[0].replace("(", ""));
2034
+ const modifierRe = /(?:^|[\s(])(\.[a-z]\w*)\b/g;
2035
+ while ((m = modifierRe.exec(sioText)) !== null) techTokens.push(m[1]);
2036
+ const camelRe = /\b[a-z]+[A-Z][A-Za-z]+\b/g;
2037
+ while ((m = camelRe.exec(sioText)) !== null) techTokens.push(m[0]);
2038
+ const existing = new Set((concept.keywords || []).map((k) => k.toLowerCase()));
2039
+ const newTokens = [...new Set(techTokens)].filter((t) => !existing.has(t.toLowerCase())).slice(0, 8);
2040
+ if (newTokens.length > 0) {
2041
+ concept.keywords = [...concept.keywords || [], ...newTokens];
2042
+ warnings.push("Concept " + concept.id + ": enriched keywords with " + newTokens.length + " SIO-derived tokens");
2043
+ }
2044
+ }
2045
+ log("STEP_5b", "SIO-keyword enrichment complete");
2046
+ }
2047
+ {
2048
+ for (const concept of concepts) {
2049
+ const sioCount = (concept.sio || []).length;
2050
+ const ptCount = (concept.problem_types || []).length;
2051
+ const prereqCount = (concept.prerequisites || []).length;
2052
+ const hasHighBloom = (concept.problem_types || []).some(
2053
+ (pt) => ["Analyze", "Evaluate", "Create"].includes(pt.bloom_level || "")
2054
+ );
2055
+ const score = Math.min(1, sioCount * 0.2 + ptCount * 0.15 + prereqCount * 0.15 + (hasHighBloom ? 0.2 : 0));
2056
+ const calibrated = Math.round(15 + score * 45);
2057
+ const llmEstimate = concept.estimated_minutes || 30;
2058
+ if (llmEstimate === 30 || Math.abs(calibrated - llmEstimate) / llmEstimate > 0.5) {
2059
+ concept.estimated_minutes = calibrated;
2060
+ if (llmEstimate !== calibrated) {
2061
+ warnings.push("Concept " + concept.id + ": minutes calibrated " + llmEstimate + "m \u2192 " + calibrated + "m (score=" + score.toFixed(2) + ")");
2062
+ }
2063
+ }
2064
+ }
2065
+ log("STEP_5c", "Minute calibration complete");
2066
+ }
1980
2067
  for (const step of learningPath) {
1981
2068
  if (!conceptIds.has(step.concept_id)) {
1982
2069
  warnings.push(`Learning path references non-existent concept ${step.concept_id}`);
@@ -2242,8 +2329,12 @@ async function decomposePhases(args) {
2242
2329
  const linkList = links.map(
2243
2330
  (l) => "- " + l.feature_id + " -> " + l.concept_id + " (" + l.depth + ")"
2244
2331
  ).join("\n") || "(none)";
2245
- const systemPrompt = 'You decompose a software project build into PROGRESSIVE COMPLETION PHASES for learning purposes.\n\nA phase is NOT a feature and NOT a milestone list: it is a cross-feature slice of the\nproduct whose completion yields a DEMONSTRABLY more complete product. Build up gradually:\nthe product grows feature by feature across phases, and each phase adds its features to\nwhat previous phases already built.\n\nRULES:\n1. Every feature must be assigned to EXACTLY ONE phase (no feature reused, none skipped).\n2. Phases are ordered; later phases depend on earlier ones and yield a more complete product.\n3. product_completion describes concretely what the product can DO once the phase is done.\n4. introduces lists which concepts the phase must teach BEFORE its feature work starts:\n - aspect new: first time the concept appears in the course\n - aspect advanced: a concept introduced in an EARLIER phase, revisited here at a\n higher depth (an advanced aspect of the same concept, e.g. validation rules on top\n of basic data modeling). Use the note field to say which aspect.\n - aspect preview: a light awareness pass ("know it exists", ULO depth only, ~5 min)\n placed in an EARLIER phase when its feature work needs the vocabulary before the\n full teach. The anchor phase still owns the full new introduction.\n5. A concept MUST be introduced (new) no later than the first phase that consumes it.\n6. A concept must not be introduced before its own prerequisites are introduced.\n7. Typically 3-6 phases for a small project; keep phases balanced.\n8. Phase 1 usually covers project setup/foundation features plus the first minimal slice.\n9. Return JSON only.';
2246
- const baseUserPrompt = "FEATURES:\n" + featureList + "\n\nCONCEPTS:\n" + conceptList + "\n\nFEATURE-CONCEPT LINKS:\n" + linkList + "\n\nReturn JSON with a phases array. Each phase: { id: PH1, name, product_completion, feature_ids: [F0, F1], introduces: [{ concept_id: C1, aspect: new or advanced, note }] }.\n";
2332
+ const systemPrompt = 'You decompose a software project build into PROGRESSIVE COMPLETION PHASES for learning purposes.\n\nA phase is NOT a feature and NOT a milestone list: it is a cross-feature slice of the\nproduct whose completion yields a DEMONSTRABLY more complete product. Build up gradually:\nthe product grows feature by feature across phases, and each phase adds its features to\nwhat previous phases already built.\n\nRULES:\n1. Every feature must be assigned to EXACTLY ONE phase (no feature reused, none skipped).\n2. Phases are ordered; later phases depend on earlier ones and yield a more complete product.\n3. product_completion describes concretely what the product can DO once the phase is done.\n4. introduces lists which concepts the phase must teach BEFORE its feature work starts:\n - aspect new: first time the concept appears in the course\n - aspect advanced: a concept introduced in an EARLIER phase, revisited here at a\n higher depth (an advanced aspect of the same concept, e.g. validation rules on top\n of basic data modeling). Use the note field to say which aspect.\n - aspect preview: a light awareness pass ("know it exists", ULO depth only, ~5 min)\n placed in an EARLIER phase when its feature work needs the vocabulary before the\n full teach. The anchor phase still owns the full new introduction.\n5. A concept MUST be introduced (new) no later than the first phase that consumes it.\n6. A concept must not be introduced before its own prerequisites are introduced.\n7. Typically 3-6 phases for a small project; keep phases balanced.\n8. MINUTE BALANCE: Each phase should contain approximately 120-240 total minutes of\n concept + feature work (about 3-5 sessions of 90 minutes each). If a phase\n would contain < 60 minutes (too thin) or > 400 minutes (too heavy), rebalance.\n9. Phase 1 usually covers project setup/foundation features plus the first minimal slice.\n10. Return JSON only.';
2333
+ const totalFeatureMinutes = features.reduce((s, f) => s + (f.steps || []).reduce((ss, st) => ss + (st.effort?.estimated_minutes || 15), 0), 0);
2334
+ const totalConceptMinutes = concepts.reduce((s, c) => s + (c.estimated_minutes || 30), 0);
2335
+ const totalMinutes = totalFeatureMinutes + totalConceptMinutes;
2336
+ const targetPhaseCount = Math.max(3, Math.round(totalMinutes / 180));
2337
+ const baseUserPrompt = "FEATURES:\n" + featureList + "\n\nCONCEPTS:\n" + conceptList + "\n\nFEATURE-CONCEPT LINKS:\n" + linkList + "\n\nTOTAL MINUTES: ~" + totalMinutes + "m (" + totalFeatureMinutes + "m features + " + totalConceptMinutes + "m concepts)\nTARGET: ~" + targetPhaseCount + " phases of ~180m each\n\nReturn JSON with a phases array. Each phase: { id: PH1, name, product_completion, feature_ids: [F0, F1], introduces: [{ concept_id: C1, aspect: new or advanced, note }] }.\n";
2247
2338
  const MAX_DECOMPOSE_ATTEMPTS = 3;
2248
2339
  let auditErrors = [];
2249
2340
  let phases = [];
@@ -2280,6 +2371,6 @@ async function decomposePhases(args) {
2280
2371
  return phases;
2281
2372
  }
2282
2373
 
2283
- export { CIO_CODE_RE, CONCEPT_CODE_RE, LEARNER_CLAUSE, SIO_CODE_RE, ULO_CODE_RE, assignConceptCodes, auditDepthLayers, auditSyllabusCoverage, breakPrerequisiteCycles, cioActionSlug, cioCode, conceptCodeFromName, createLlmClient, detectPrerequisiteCycles, escalateAndMapConcepts, extractFeatureSteps, extractFeatureStepsBatched, extractProjectOverview, extractScaffold, generateHybridGraph, generateKnowledgeGraph, llmChatJson, sioCode, standardStatement, techTagFor, uloCode, verifyKnowledgeGraph, verifyProjectGraph };
2284
- //# sourceMappingURL=chunk-Y7MRFFDJ.mjs.map
2285
- //# sourceMappingURL=chunk-Y7MRFFDJ.mjs.map
2374
+ export { CIO_CODE_RE, CONCEPT_CODE_RE, LEARNER_CLAUSE, SIO_CODE_RE, ULO_CODE_RE, assignConceptCodes, auditDepthLayers, auditPhasePlan, auditSyllabusCoverage, breakPrerequisiteCycles, cioActionSlug, cioCode, conceptCodeFromName, createLlmClient, detectPrerequisiteCycles, escalateAndMapConcepts, extractFeatureSteps, extractFeatureStepsBatched, extractProjectOverview, extractScaffold, generateHybridGraph, generateKnowledgeGraph, llmChatJson, sioCode, standardStatement, techTagFor, uloCode, verifyKnowledgeGraph, verifyProjectGraph };
2375
+ //# sourceMappingURL=chunk-TYXSJUQU.mjs.map
2376
+ //# sourceMappingURL=chunk-TYXSJUQU.mjs.map