@thanh01.pmt/domain-kit 0.5.0 → 0.6.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 (69) hide show
  1. package/README.md +682 -168
  2. package/dist/assembly/index.d.cts +2 -2
  3. package/dist/assembly/index.d.ts +2 -2
  4. package/dist/{chunk-TYXSJUQU.mjs → chunk-2OGNQUXC.mjs} +81 -32
  5. package/dist/chunk-2OGNQUXC.mjs.map +1 -0
  6. package/dist/{chunk-G7HLFMD2.mjs → chunk-5OMSYKQP.mjs} +43 -23
  7. package/dist/chunk-5OMSYKQP.mjs.map +1 -0
  8. package/dist/{chunk-BL5NBKPH.mjs → chunk-67GXK3ZE.mjs} +18 -8
  9. package/dist/chunk-67GXK3ZE.mjs.map +1 -0
  10. package/dist/{chunk-E4GCCWJO.mjs → chunk-CGNXGVHV.mjs} +37 -4
  11. package/dist/chunk-CGNXGVHV.mjs.map +1 -0
  12. package/dist/{chunk-VCPIWJ7R.mjs → chunk-ERDTHXQA.mjs} +21 -3
  13. package/dist/chunk-ERDTHXQA.mjs.map +1 -0
  14. package/dist/chunk-OYSZQVPY.mjs +10 -0
  15. package/dist/chunk-OYSZQVPY.mjs.map +1 -0
  16. package/dist/{chunk-TWKIVUTQ.mjs → chunk-WKTH3JPA.mjs} +197 -36
  17. package/dist/chunk-WKTH3JPA.mjs.map +1 -0
  18. package/dist/{conceptEscalator-YfMKIKCZ.d.ts → conceptEscalator-BCsw0ys_.d.ts} +10 -1
  19. package/dist/{conceptEscalator-ltRLtPhf.d.cts → conceptEscalator-DPrs4PwC.d.cts} +10 -1
  20. package/dist/concepts/index.d.cts +1 -1
  21. package/dist/concepts/index.d.ts +1 -1
  22. package/dist/{curriculumFeedSchema-DUFwFyr2.d.ts → curriculumFeedSchema-DYayelAA.d.cts} +156 -1
  23. package/dist/{curriculumFeedSchema-DUFwFyr2.d.cts → curriculumFeedSchema-DYayelAA.d.ts} +156 -1
  24. package/dist/detector/index.cjs +21 -6
  25. package/dist/detector/index.cjs.map +1 -1
  26. package/dist/detector/index.mjs +2 -1
  27. package/dist/feed/index.cjs +229 -33
  28. package/dist/feed/index.cjs.map +1 -1
  29. package/dist/feed/index.d.cts +32 -2
  30. package/dist/feed/index.d.ts +32 -2
  31. package/dist/feed/index.mjs +2 -2
  32. package/dist/graph/index.cjs +73 -29
  33. package/dist/graph/index.cjs.map +1 -1
  34. package/dist/graph/index.d.cts +14 -5
  35. package/dist/graph/index.d.ts +14 -5
  36. package/dist/graph/index.mjs +2 -1
  37. package/dist/{hybridGraphPipeline-DXQDbBEj.d.ts → hybridGraphPipeline-9dRKJj7r.d.ts} +6 -2
  38. package/dist/{hybridGraphPipeline-dT-bio-3.d.cts → hybridGraphPipeline-CocfYe4d.d.cts} +6 -2
  39. package/dist/{hybridGraphSchema-nrUeSwpU.d.ts → hybridGraphSchema-C37qxdTM.d.cts} +16 -3
  40. package/dist/{hybridGraphSchema-nrUeSwpU.d.cts → hybridGraphSchema-C37qxdTM.d.ts} +16 -3
  41. package/dist/index.cjs +2634 -2359
  42. package/dist/index.cjs.map +1 -1
  43. package/dist/index.d.cts +6 -6
  44. package/dist/index.d.ts +6 -6
  45. package/dist/index.mjs +7 -6
  46. package/dist/llmClient-CX5uUiQ1.d.cts +60 -0
  47. package/dist/llmClient-CX5uUiQ1.d.ts +60 -0
  48. package/dist/pipeline/index.cjs +2207 -1958
  49. package/dist/pipeline/index.cjs.map +1 -1
  50. package/dist/pipeline/index.d.cts +13 -5
  51. package/dist/pipeline/index.d.ts +13 -5
  52. package/dist/pipeline/index.mjs +6 -5
  53. package/dist/schemas/index.cjs +55 -2
  54. package/dist/schemas/index.cjs.map +1 -1
  55. package/dist/schemas/index.d.cts +2 -2
  56. package/dist/schemas/index.d.ts +2 -2
  57. package/dist/schemas/index.mjs +2 -2
  58. package/package.json +10 -9
  59. package/LICENSE +0 -21
  60. package/dist/chunk-BL5NBKPH.mjs.map +0 -1
  61. package/dist/chunk-E4GCCWJO.mjs.map +0 -1
  62. package/dist/chunk-G7HLFMD2.mjs.map +0 -1
  63. package/dist/chunk-TWKIVUTQ.mjs.map +0 -1
  64. package/dist/chunk-TYXSJUQU.mjs.map +0 -1
  65. package/dist/chunk-VCPIWJ7R.mjs.map +0 -1
  66. package/dist/curriculumFeedEmitter-T7NUBLAM.mjs +0 -4
  67. package/dist/curriculumFeedEmitter-T7NUBLAM.mjs.map +0 -1
  68. package/dist/llmClient-ysPhLjcH.d.cts +0 -16
  69. package/dist/llmClient-ysPhLjcH.d.ts +0 -16
@@ -1,18 +1,82 @@
1
- import { validateCurriculumFeed } from './chunk-E4GCCWJO.mjs';
1
+ import { validateCurriculumFeed } from './chunk-CGNXGVHV.mjs';
2
2
 
3
3
  // src/feed/curriculumFeedEmitter.ts
4
+ var DEPTH_MINUTE_FRACTIONS = {
5
+ /** Awareness-only pass before the anchor teach. */
6
+ preview: 0.25,
7
+ ulo: 0.35,
8
+ cio: 0.65,
9
+ sio: 1,
10
+ /** An advanced revisit re-teaches the concept, not the whole thing again. */
11
+ advancedRevisit: 0.5,
12
+ /** Minutes saved when an advanced revisit drops from SIO practice to a CIO recap. */
13
+ advancedRecapSave: 0.3,
14
+ /** Floor so a tiny concept still gets a teachable awareness pass. */
15
+ floor: 5,
16
+ /** Floor for an advanced revisit — it is a real teach, not an awareness pass. */
17
+ advancedFloor: 15
18
+ };
19
+ var DEFAULT_STEP_MINUTES = 15;
4
20
  var BLOOM_RANK = { Remember: 0, Understand: 1, Apply: 2, Analyze: 3, Evaluate: 4, Create: 5 };
5
21
  var pad2 = (n) => String(n).padStart(2, "0");
22
+ var steplessStepIds = [];
23
+ var takeSteplessStepIds = () => steplessStepIds.splice(0, steplessStepIds.length);
6
24
  var arr = (v) => Array.isArray(v) ? v : [];
7
25
  var str = (v, d = "") => typeof v === "string" ? v : d;
8
26
  var num = (v, d) => typeof v === "number" && Number.isFinite(v) ? v : d;
9
27
  var strs = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
10
28
  function stepMinutes(step) {
11
29
  const effort = step.effort ?? {};
12
- return Math.max(0, Math.round(num(effort.estimated_minutes, num(step.estimated_minutes, 45))));
30
+ return Math.max(0, Math.round(num(effort.estimated_minutes, num(step.estimated_minutes, DEFAULT_STEP_MINUTES))));
13
31
  }
14
- function pushWarning(opts, message) {
15
- (opts.warnings ?? (opts.warnings = [])).push(message);
32
+ function diagnosticsOf(opts) {
33
+ const list = opts.diagnostics ?? (opts.diagnostics = []);
34
+ for (const message of opts.warnings ?? []) {
35
+ if (!list.some((d) => d.code === "upstream_warning" && d.message === message)) {
36
+ list.push({ code: "upstream_warning", severity: "warning", message, node_ids: [] });
37
+ }
38
+ }
39
+ return list;
40
+ }
41
+ function pushDiagnostic(opts, code, severity, message, nodeIds = []) {
42
+ diagnosticsOf(opts).push({ code, severity, message, node_ids: nodeIds });
43
+ }
44
+ function splitPrerequisites(concept, known) {
45
+ const internal = [];
46
+ const external = [];
47
+ for (const p of strs(concept.prerequisites)) {
48
+ (known.has(p) ? internal : external).push(p);
49
+ }
50
+ return { internal, external };
51
+ }
52
+ function noteExternalPrerequisites(opts, byNode) {
53
+ if (byNode.size === 0) return;
54
+ const entries = [];
55
+ for (const [nodeId, prereqs] of byNode) {
56
+ for (const prereqId of prereqs) entries.push({ nodeId, prereqId });
57
+ }
58
+ entries.sort((a, b) => a.nodeId === b.nodeId ? a.prereqId.localeCompare(b.prereqId) : a.nodeId.localeCompare(b.nodeId));
59
+ pushDiagnostic(
60
+ opts,
61
+ "external_prerequisite",
62
+ "info",
63
+ "Prerequisite(s) outside this analysis \u2014 the learner must already know them: " + entries.map((e) => e.nodeId + " \u2190 " + e.prereqId).join(", "),
64
+ entries.map((e) => e.nodeId)
65
+ );
66
+ }
67
+ function buildSource(opts, graphType) {
68
+ const diagnostics = diagnosticsOf(opts);
69
+ return {
70
+ graph_type: graphType,
71
+ // Derived view — 'info' diagnostics are deliberately not surfaced as warnings.
72
+ warnings: diagnostics.filter((d) => d.severity !== "info").map((d) => d.message),
73
+ hallucination_count: opts.hallucinationCount ?? 0,
74
+ diagnostics,
75
+ provenance: opts.provenance
76
+ };
77
+ }
78
+ function keywordKey(k) {
79
+ return k.normalize("NFC").toLowerCase();
16
80
  }
17
81
  function newKeywordsInOrder(nodes) {
18
82
  const seen = /* @__PURE__ */ new Set();
@@ -21,9 +85,9 @@ function newKeywordsInOrder(nodes) {
21
85
  n.keywords.new = [];
22
86
  n.keywords.prerequisite = [];
23
87
  for (const k of all) {
24
- if (seen.has(k)) n.keywords.prerequisite.push(k);
88
+ if (seen.has(keywordKey(k))) n.keywords.prerequisite.push(k);
25
89
  else n.keywords.new.push(k);
26
- seen.add(k);
90
+ seen.add(keywordKey(k));
27
91
  }
28
92
  }
29
93
  }
@@ -47,6 +111,7 @@ function feedNodeFromStep(step, featureId) {
47
111
  // sessions at user-visible checkpoints instead of raw minute counts.
48
112
  user_visible_deliverable: str(step.outcome?.user_visible),
49
113
  is_core: false,
114
+ external_prerequisites: [],
50
115
  phase_id: "",
51
116
  introduce_aspect: null
52
117
  };
@@ -66,9 +131,9 @@ function deriveConceptDepthHint(concept) {
66
131
  return "ulo";
67
132
  }
68
133
  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);
134
+ const ulo = Math.max(DEPTH_MINUTE_FRACTIONS.floor, Math.round(minutes * DEPTH_MINUTE_FRACTIONS.ulo));
135
+ const cio = Math.max(ulo + DEPTH_MINUTE_FRACTIONS.floor, Math.round(minutes * DEPTH_MINUTE_FRACTIONS.cio));
136
+ const sio = Math.max(cio + DEPTH_MINUTE_FRACTIONS.floor, minutes);
72
137
  const variants = { ulo, cio, sio };
73
138
  const candidates = [];
74
139
  if (depth === "sio") {
@@ -111,7 +176,11 @@ function feedNodeFromConcept(concept) {
111
176
  user_visible_deliverable: "",
112
177
  // concepts are taught, not shipped — checkpoints live on product steps
113
178
  ...deriveDepthInputs(Math.max(0, Math.round(num(concept.estimated_minutes, 15))), deriveConceptDepthHint(concept)),
114
- is_core: false,
179
+ // Knowledge-Tree concepts are the spine of the course — the reconciler must
180
+ // escalate them, never downgrade them. The emitter hardcoded `false`, so the
181
+ // flag the schema documents never reached the planner for any concept.
182
+ is_core: concept.is_core === true || str(concept.standardization) === "standardized",
183
+ external_prerequisites: [],
115
184
  depth_context: {
116
185
  ulo: str(concept.ulo),
117
186
  cio: str(concept.cio),
@@ -127,7 +196,10 @@ function addFeatureGroup(f, fid, nodes, edges, groupings) {
127
196
  let prev = "";
128
197
  for (let i = 0; i < steps.length; i++) {
129
198
  const stepNode = feedNodeFromStep(steps[i], fid);
130
- if (!stepNode.id) continue;
199
+ if (!stepNode.id) {
200
+ steplessStepIds.push(fid + "#" + i);
201
+ continue;
202
+ }
131
203
  stepIds.push(stepNode.id);
132
204
  nodes.push(stepNode);
133
205
  if (prev) edges.push({ from: prev, to: stepNode.id, kind: "task", reason: "feature build order" });
@@ -150,18 +222,26 @@ function emitFromHybrid(hybrid, opts) {
150
222
  const features = arr(pg.features);
151
223
  const conceptRecs = arr(kg.concepts);
152
224
  const conceptById = new Map(conceptRecs.map((c) => [str(c.id), c]));
225
+ const knownConceptIds = new Set(conceptById.keys());
153
226
  if (rawPhases.length === 0) {
227
+ const externalByNode2 = /* @__PURE__ */ new Map();
154
228
  for (const c of conceptRecs) {
155
229
  const node = feedNodeFromConcept(c);
156
230
  nodes.push(node);
157
- for (const p of strs(c.prerequisites)) edges.push({ from: p, to: node.id, kind: "knowledge", reason: "concept prerequisite" });
231
+ const { internal, external } = splitPrerequisites(c, knownConceptIds);
232
+ if (external.length > 0) {
233
+ node.external_prerequisites = external;
234
+ externalByNode2.set(node.id, external);
235
+ }
236
+ for (const p of internal) edges.push({ from: p, to: node.id, kind: "knowledge", reason: "concept prerequisite" });
158
237
  }
238
+ noteExternalPrerequisites(opts, externalByNode2);
159
239
  features.forEach((f, fi) => addFeatureGroup(f, str(f.id, "F" + pad2(fi + 1)), nodes, edges, groupings));
160
240
  linkHybridKnowledgeEdges(hybrid, nodes, edges);
161
241
  newKeywordsInOrder(nodes);
162
242
  return validateCurriculumFeed({
163
243
  schema_version: 2,
164
- source: { graph_type: "hybrid_graph", warnings: opts.warnings ?? [], hallucination_count: opts.hallucinationCount ?? 0 },
244
+ source: buildSource(opts, "hybrid_graph"),
165
245
  learning_nodes: nodes,
166
246
  dependency_edges: edges,
167
247
  suggested_groupings: groupings,
@@ -178,6 +258,8 @@ function emitFromHybrid(hybrid, opts) {
178
258
  const emitCtx = [];
179
259
  const emittedConcepts = /* @__PURE__ */ new Set();
180
260
  const previewsEmitted = /* @__PURE__ */ new Set();
261
+ const previewNodeIdByConcept = /* @__PURE__ */ new Map();
262
+ const externalByNode = /* @__PURE__ */ new Map();
181
263
  const linkList = arr(hybrid.links);
182
264
  for (const rp of rawPhases) {
183
265
  const pid = str(rp.id);
@@ -217,7 +299,7 @@ function emitFromHybrid(hybrid, opts) {
217
299
  prevNode.name = str(c.name, cid) + " (preview)";
218
300
  prevNode.phase_id = pid;
219
301
  prevNode.introduce_aspect = "preview";
220
- prevNode.estimated_minutes = Math.max(5, Math.round(prevNode.estimated_minutes * 0.25));
302
+ prevNode.estimated_minutes = Math.max(DEPTH_MINUTE_FRACTIONS.floor, Math.round(prevNode.estimated_minutes * DEPTH_MINUTE_FRACTIONS.preview));
221
303
  prevNode.depth_hint = "ulo";
222
304
  prevNode.bloom_hint = "Understand";
223
305
  prevNode.concept_codes = [str(c.code) || cid];
@@ -227,6 +309,7 @@ function emitFromHybrid(hybrid, opts) {
227
309
  prevNode.depth_scaffold_candidates = [];
228
310
  nodes.push(prevNode);
229
311
  conceptOrder.push(prevNode.id);
312
+ previewNodeIdByConcept.set(cid, prevNode.id);
230
313
  continue;
231
314
  }
232
315
  if (!emittedConcepts.has(cid)) {
@@ -235,13 +318,22 @@ function emitFromHybrid(hybrid, opts) {
235
318
  node.introduce_aspect = "new";
236
319
  nodes.push(node);
237
320
  for (const p of strs(c.prerequisites)) {
238
- if (!emittedConcepts.has(p)) {
239
- throw new Error("Concept " + cid + " (phase " + pid + ") has prerequisite " + p + " that is not introduced in an earlier phase - fail-closed");
321
+ if (!conceptById.has(p)) {
322
+ if (!externalByNode.has(cid)) externalByNode.set(cid, []);
323
+ externalByNode.get(cid).push(p);
324
+ node.external_prerequisites.push(p);
325
+ continue;
326
+ }
327
+ const previewOfPrereq = previewNodeIdByConcept.get(p);
328
+ if (!emittedConcepts.has(p) && !previewOfPrereq) {
329
+ throw new Error("Concept " + cid + " (phase " + pid + ") has prerequisite " + p + " that is neither introduced nor previewed in an EARLIER phase - fail-closed");
240
330
  }
241
- edges.push({ from: p, to: cid, kind: "knowledge", reason: "concept prerequisite" });
331
+ edges.push({ from: emittedConcepts.has(p) ? p : previewOfPrereq, to: cid, kind: "knowledge", reason: "concept prerequisite" });
242
332
  }
243
333
  emittedConcepts.add(cid);
244
334
  conceptOrder.push(cid);
335
+ } else if (str(intro.aspect, "new") === "new") {
336
+ throw new Error("Phase " + pid + " introduces " + cid + " as new but it was already introduced in an earlier phase - fail-closed");
245
337
  } else {
246
338
  const revisitId = cid + "__ADV" + pad2(order);
247
339
  const node = feedNodeFromConcept(c);
@@ -249,8 +341,16 @@ function emitFromHybrid(hybrid, opts) {
249
341
  node.name = str(c.name, cid) + " (advanced)";
250
342
  node.phase_id = pid;
251
343
  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" }];
253
- node.estimated_minutes = Math.max(15, Math.round(node.estimated_minutes * 0.5));
344
+ node.estimated_minutes = Math.max(
345
+ DEPTH_MINUTE_FRACTIONS.advancedFloor,
346
+ Math.round(node.estimated_minutes * DEPTH_MINUTE_FRACTIONS.advancedRevisit)
347
+ );
348
+ node.depth_scaffold_candidates = [{
349
+ from_depth: "sio",
350
+ to_depth: "cio",
351
+ minutes_saved: Math.round(node.estimated_minutes * DEPTH_MINUTE_FRACTIONS.advancedRecapSave),
352
+ reason: "advanced revisit may drop to CIO recap without breaking first-teach chain"
353
+ }];
254
354
  node.concept_codes = [str(c.code) || cid];
255
355
  edges.push({ from: cid, to: revisitId, kind: "knowledge", reason: "advanced revisit of " + cid + " at phase " + pid });
256
356
  nodes.push(node);
@@ -260,6 +360,7 @@ function emitFromHybrid(hybrid, opts) {
260
360
  const featureIds = strs(rp.feature_ids).filter((fid) => featureOrder.has(fid)).sort((a, b) => featureOrder.get(a) - featureOrder.get(b));
261
361
  emitCtx.push({ id: pid, order, name: str(rp.name, pid), productCompletion: str(rp.product_completion), conceptOrder, featureIds });
262
362
  }
363
+ noteExternalPrerequisites(opts, externalByNode);
263
364
  for (const l of linkList) {
264
365
  const cid = str(l.concept_id);
265
366
  if (cid && conceptById.has(cid) && !emittedConcepts.has(cid)) {
@@ -271,6 +372,16 @@ function emitFromHybrid(hybrid, opts) {
271
372
  if (missingFeatures.length > 0) {
272
373
  throw new Error("Features not covered by any phase: " + missingFeatures.join(", ") + " - fail-closed");
273
374
  }
375
+ const orphans = Array.from(conceptById.keys()).filter((cid) => !emittedConcepts.has(cid));
376
+ if (orphans.length > 0) {
377
+ pushDiagnostic(
378
+ opts,
379
+ "orphan_concept",
380
+ "error",
381
+ "Knowledge graph has " + orphans.length + " concept(s) no phase introduces and no link consumes \u2014 they will not be taught: " + orphans.join(", "),
382
+ orphans
383
+ );
384
+ }
274
385
  const phaseNodeIds = [];
275
386
  for (const ctx of emitCtx) {
276
387
  const ids = [...ctx.conceptOrder];
@@ -278,13 +389,26 @@ function emitFromHybrid(hybrid, opts) {
278
389
  const f = features.find((x) => str(x.id) === fid);
279
390
  const stepIds = addFeatureGroup(f, fid, nodes, edges, groupings);
280
391
  for (const sid of stepIds) {
281
- const node = nodes.find((n) => n.id === sid);
392
+ const node = nodes.find((x) => x.id === sid);
282
393
  node.phase_id = ctx.id;
283
394
  }
284
395
  ids.push(...stepIds);
285
396
  }
397
+ if (ids.length === 0) {
398
+ throw new Error("Phase " + ctx.id + " contains no teaching nodes (no concepts introduced and no feature steps) - drop it or give it work - fail-closed");
399
+ }
286
400
  phaseNodeIds.push(ids);
287
401
  }
402
+ const idlessInHybrid = takeSteplessStepIds();
403
+ if (idlessInHybrid.length > 0) {
404
+ pushDiagnostic(
405
+ opts,
406
+ "step_without_id",
407
+ "warning",
408
+ "Step(s) carrying no id cannot become planning nodes and were dropped (feature#index): " + idlessInHybrid.join(", "),
409
+ idlessInHybrid
410
+ );
411
+ }
288
412
  const placed = new Set(phaseNodeIds.flat());
289
413
  const orderedNodes = [];
290
414
  for (const ids of phaseNodeIds) {
@@ -315,7 +439,13 @@ function emitFromHybrid(hybrid, opts) {
315
439
  const anchorPhaseIdx = anchorNode ? emitCtx.findIndex((c2) => c2.id === anchorNode.phase_id) : -1;
316
440
  const stepPhaseIdx = stepNode ? emitCtx.findIndex((c2) => c2.id === stepNode.phase_id) : -1;
317
441
  if (emitCtx.length > 0 && anchorPhaseIdx > stepPhaseIdx) {
318
- pushWarning(opts, "hybrid link drift: concept " + cid + " is fully taught in a later phase than consumer " + firstStep + " (a preview covers the dependency)");
442
+ pushDiagnostic(
443
+ opts,
444
+ "hybrid_link_drift",
445
+ "warning",
446
+ "hybrid link drift: concept " + cid + " is fully taught in a later phase than consumer " + firstStep + " (a preview covers the dependency)",
447
+ [cid, firstStep]
448
+ );
319
449
  continue;
320
450
  }
321
451
  pushEdge(cid, firstStep, str(l.relationship, "hybrid link") + " [" + depth + "]");
@@ -329,12 +459,12 @@ function emitFromHybrid(hybrid, opts) {
329
459
  const feedOrder = new Map(nodes.map((n, i) => [n.id, i]));
330
460
  const introducer = /* @__PURE__ */ new Map();
331
461
  for (const n of nodes) for (const k of n.keywords?.new ?? []) {
332
- const key = k.toLowerCase();
462
+ const key = keywordKey(k);
333
463
  if (!introducer.has(key)) introducer.set(key, n.id);
334
464
  }
335
465
  for (const n of nodes) {
336
466
  for (const k of n.keywords?.prerequisite ?? []) {
337
- const src = introducer.get(k.toLowerCase());
467
+ const src = introducer.get(keywordKey(k));
338
468
  if (!src || src === n.id) continue;
339
469
  const srcNode = nodes.find((x) => x.id === src);
340
470
  const phaseOf = (id) => {
@@ -345,9 +475,12 @@ function emitFromHybrid(hybrid, opts) {
345
475
  const fromPhase = phaseOf(src);
346
476
  const toPhase = phaseOf(n.id);
347
477
  if (conceptOvertaken || emitCtx.length > 0 && fromPhase > toPhase) {
348
- pushWarning(
478
+ pushDiagnostic(
349
479
  opts,
350
- "keyword-order drift: introducer " + src + " sits in a later phase than consumer " + n.id + ' (keyword "' + k + '") \u2014 host should move the introducer earlier'
480
+ conceptOvertaken ? "concept_overtaken_by_step" : "keyword_order_drift",
481
+ "warning",
482
+ "keyword-order drift: introducer " + src + " sits in a later phase than consumer " + n.id + ' (keyword "' + k + '") \u2014 host should move the introducer earlier',
483
+ [src, n.id]
351
484
  );
352
485
  continue;
353
486
  }
@@ -363,7 +496,7 @@ function emitFromHybrid(hybrid, opts) {
363
496
  }));
364
497
  return validateCurriculumFeed({
365
498
  schema_version: 2,
366
- source: { graph_type: "hybrid_graph", warnings: opts.warnings ?? [], hallucination_count: opts.hallucinationCount ?? 0 },
499
+ source: buildSource(opts, "hybrid_graph"),
367
500
  learning_nodes: nodes,
368
501
  dependency_edges: edges,
369
502
  suggested_groupings: groupings,
@@ -391,11 +524,17 @@ function emitFromKnowledge(kg, opts) {
391
524
  const edges = [];
392
525
  const groupings = [];
393
526
  const conceptIds = new Set(arr(kg.concepts).map((c) => str(c.id)));
527
+ const externalByNode = /* @__PURE__ */ new Map();
394
528
  for (const c of arr(kg.concepts)) {
395
529
  const node = feedNodeFromConcept(c);
396
530
  nodes.push(node);
397
- for (const p of strs(c.prerequisites)) {
398
- if (p !== node.id && conceptIds.has(p)) {
531
+ const { internal, external } = splitPrerequisites(c, conceptIds);
532
+ if (external.length > 0) {
533
+ node.external_prerequisites = external;
534
+ externalByNode.set(node.id, external);
535
+ }
536
+ for (const p of internal) {
537
+ if (p !== node.id) {
399
538
  edges.push({
400
539
  from: p,
401
540
  to: node.id,
@@ -405,6 +544,7 @@ function emitFromKnowledge(kg, opts) {
405
544
  }
406
545
  }
407
546
  }
547
+ noteExternalPrerequisites(opts, externalByNode);
408
548
  if (arr(kg.learning_path).length > 0) {
409
549
  const orderMap = /* @__PURE__ */ new Map();
410
550
  arr(kg.learning_path).forEach((s, idx) => {
@@ -429,7 +569,7 @@ function emitFromKnowledge(kg, opts) {
429
569
  newKeywordsInOrder(nodes);
430
570
  return validateCurriculumFeed({
431
571
  schema_version: 2,
432
- source: { graph_type: "knowledge_graph", warnings: opts.warnings ?? [], hallucination_count: opts.hallucinationCount ?? 0 },
572
+ source: buildSource(opts, "knowledge_graph"),
433
573
  learning_nodes: nodes,
434
574
  dependency_edges: edges,
435
575
  suggested_groupings: groupings,
@@ -440,17 +580,38 @@ function emitFromProject(pg, opts) {
440
580
  const nodes = [];
441
581
  const edges = [];
442
582
  const groupings = [];
443
- arr(pg.features).forEach((f, fi) => addFeatureGroup(f, str(f.id, "F" + pad2(fi + 1)), nodes, edges, groupings));
444
- if (nodes.length === 0 && arr(pg.features).length > 0) {
583
+ const allFeatures = arr(pg.features);
584
+ allFeatures.forEach((f, fi) => addFeatureGroup(f, str(f.id, "F" + pad2(fi + 1)), nodes, edges, groupings));
585
+ const stepless = allFeatures.filter((f) => arr(f.steps).length === 0).map((f) => str(f.id)).filter(Boolean);
586
+ if (stepless.length > 0) {
587
+ pushDiagnostic(
588
+ opts,
589
+ "feature_without_steps",
590
+ "warning",
591
+ "Feature(s) declared but carrying no steps \u2014 they will be absent from the feed: " + stepless.join(", "),
592
+ stepless
593
+ );
594
+ }
595
+ const idless = takeSteplessStepIds();
596
+ if (idless.length > 0) {
597
+ pushDiagnostic(
598
+ opts,
599
+ "step_without_id",
600
+ "warning",
601
+ "Step(s) carrying no id cannot become planning nodes and were dropped (feature#index): " + idless.join(", "),
602
+ idless
603
+ );
604
+ }
605
+ if (nodes.length === 0 && allFeatures.length > 0) {
445
606
  const hasTasks = arr(pg.implementation ?? {}).length > 0 || arr(pg.implementation?.tasks).length > 0;
446
607
  throw new Error(
447
- "Project graph has " + arr(pg.features).length + " features but NO features[].steps - the curriculum feed cannot emit planning nodes." + (hasTasks ? " This looks like a legacy knowledge-tree graph (implementation.tasks). Regenerate it with the domain-kit v3 pipeline (runProjectGraphPipeline / hybridGraphPipeline), then re-run." : " Every feature must carry at least one step with id/name/keywords/effort for curriculum planning.")
608
+ "Project graph has " + allFeatures.length + " features but NO features[].steps - the curriculum feed cannot emit planning nodes." + (hasTasks ? " This looks like a legacy knowledge-tree graph (implementation.tasks). Regenerate it with the domain-kit v3 pipeline (runProjectGraphPipeline / hybridGraphPipeline), then re-run." : " Every feature must carry at least one step with id/name/keywords/effort for curriculum planning.")
448
609
  );
449
610
  }
450
611
  newKeywordsInOrder(nodes);
451
612
  return validateCurriculumFeed({
452
613
  schema_version: 2,
453
- source: { graph_type: "project_graph", warnings: opts.warnings ?? [], hallucination_count: opts.hallucinationCount ?? 0 },
614
+ source: buildSource(opts, "project_graph"),
454
615
  learning_nodes: nodes,
455
616
  dependency_edges: edges,
456
617
  suggested_groupings: groupings,
@@ -469,6 +630,6 @@ function emitCurriculumFeed(graph, opts = {}) {
469
630
  return emitFromProject(g, opts);
470
631
  }
471
632
 
472
- export { emitCurriculumFeed };
473
- //# sourceMappingURL=chunk-TWKIVUTQ.mjs.map
474
- //# sourceMappingURL=chunk-TWKIVUTQ.mjs.map
633
+ export { DEFAULT_STEP_MINUTES, DEPTH_MINUTE_FRACTIONS, emitCurriculumFeed };
634
+ //# sourceMappingURL=chunk-WKTH3JPA.mjs.map
635
+ //# sourceMappingURL=chunk-WKTH3JPA.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/feed/curriculumFeedEmitter.ts"],"names":["externalByNode"],"mappings":"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","file":"chunk-WKTH3JPA.mjs","sourcesContent":["import {\n validateCurriculumFeed, type CurriculumFeed, type FeedNode, type FeedEdge, type FeedGrouping,\n type FeedDiagnostic, type FeedProvenance,\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 /** Upstream (graph pipeline) warnings — imported as 'upstream_warning' diagnostics. */\n warnings?: string[];\n diagnostics?: FeedDiagnostic[];\n provenance?: FeedProvenance;\n}\n\n/**\n * Minute fractions per depth level. ONE table: the preview node, the depth_variants\n * price list and the advanced-revisit downgrade candidate all read from here. They\n * used to be 25% / 35% / 65% / 100% / 30% hardcoded in three places, which meant\n * \"what does a ULO pass cost?\" had three answers.\n */\nexport const DEPTH_MINUTE_FRACTIONS = {\n /** Awareness-only pass before the anchor teach. */\n preview: 0.25,\n ulo: 0.35,\n cio: 0.65,\n sio: 1.0,\n /** An advanced revisit re-teaches the concept, not the whole thing again. */\n advancedRevisit: 0.5,\n /** Minutes saved when an advanced revisit drops from SIO practice to a CIO recap. */\n advancedRecapSave: 0.3,\n /** Floor so a tiny concept still gets a teachable awareness pass. */\n floor: 5,\n /** Floor for an advanced revisit — it is a real teach, not an awareness pass. */\n advancedFloor: 15,\n} as const;\n\n/**\n * Fallback when a step declares no `effort.estimated_minutes`. The emitter and the\n * hybrid totals used to disagree (45 vs 15) on the same missing field, so the feed\n * and the planning total priced the same step differently.\n */\nexport const DEFAULT_STEP_MINUTES = 15;\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\n/** Steps dropped during emission because they carried no id — reported per run. */\nlet steplessStepIds: string[] = [];\nconst takeSteplessStepIds = (): string[] => steplessStepIds.splice(0, steplessStepIds.length);\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, DEFAULT_STEP_MINUTES))));\n}\n\nfunction diagnosticsOf(opts: EmitFeedOptions): FeedDiagnostic[] {\n const list = opts.diagnostics ?? (opts.diagnostics = []);\n // Upstream warnings enter the SAME list once, so `source.diagnostics` is the one\n // record and `source.warnings` can stay a derived text view for old consumers.\n for (const message of opts.warnings ?? []) {\n if (!list.some((d) => d.code === 'upstream_warning' && d.message === message)) {\n list.push({ code: 'upstream_warning', severity: 'warning', message, node_ids: [] });\n }\n }\n return list;\n}\n\n/** The single writer for emission findings. Every code is greppable. */\nfunction pushDiagnostic(\n opts: EmitFeedOptions,\n code: string,\n severity: FeedDiagnostic['severity'],\n message: string,\n nodeIds: string[] = [],\n): void {\n diagnosticsOf(opts).push({ code, severity, message, node_ids: nodeIds });\n}\n\n/**\n * Prerequisites that no node in this feed provides.\n *\n * The three emission paths used to disagree about the same input: the knowledge\n * path silently dropped them, while BOTH hybrid paths hard-failed — the no-phase\n * path built a dangling edge (\"edge C_OUTSIDE -> C2 references unknown node\") and\n * the phase path threw an *ordering* error blaming a concept that is not in the\n * analysis at all. Either way the course lost its entry requirements: the single\n * most useful piece of information for someone deciding whether to enrol.\n */\ninterface ExternalPrereq {\n nodeId: string;\n prereqId: string;\n}\n\nfunction splitPrerequisites(concept: Rec, known: Set<string>): { internal: string[]; external: string[] } {\n const internal: string[] = [];\n const external: string[] = [];\n for (const p of strs(concept.prerequisites)) {\n (known.has(p) ? internal : external).push(p);\n }\n return { internal, external };\n}\n\n/** Fills `external_prerequisites` on each node and reports the set once. */\nfunction noteExternalPrerequisites(\n opts: EmitFeedOptions,\n byNode: Map<string, string[]>,\n): void {\n if (byNode.size === 0) return;\n const entries: ExternalPrereq[] = [];\n for (const [nodeId, prereqs] of byNode) {\n for (const prereqId of prereqs) entries.push({ nodeId, prereqId });\n }\n entries.sort((a, b) => (a.nodeId === b.nodeId ? a.prereqId.localeCompare(b.prereqId) : a.nodeId.localeCompare(b.nodeId)));\n pushDiagnostic(\n opts, 'external_prerequisite', 'info',\n 'Prerequisite(s) outside this analysis — the learner must already know them: '\n + entries.map((e) => e.nodeId + ' ← ' + e.prereqId).join(', '),\n entries.map((e) => e.nodeId),\n );\n}\n\nfunction buildSource(opts: EmitFeedOptions, graphType: CurriculumFeed['source']['graph_type']) {\n const diagnostics = diagnosticsOf(opts);\n return {\n graph_type: graphType,\n // Derived view — 'info' diagnostics are deliberately not surfaced as warnings.\n warnings: diagnostics.filter((d) => d.severity !== 'info').map((d) => d.message),\n hallucination_count: opts.hallucinationCount ?? 0,\n diagnostics,\n provenance: opts.provenance,\n };\n}\n\n/** Comparison key for a keyword: NFC + case-folded, so `SwiftUI` and `swiftui`\n * are the same keyword. The edge materializer already lowercased before looking\n * up an introducer — the ledger it was compared against did not, so a case\n * difference made the ledger report two new keywords while the edge pass saw\n * one introducer. For Vietnamese this also collapses NFD/NFC spellings. */\nfunction keywordKey(k: string): string {\n return k.normalize('NFC').toLowerCase();\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(keywordKey(k))) n.keywords.prerequisite.push(k);\n else n.keywords.new.push(k);\n seen.add(keywordKey(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 external_prerequisites: [],\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(DEPTH_MINUTE_FRACTIONS.floor, Math.round(minutes * DEPTH_MINUTE_FRACTIONS.ulo));\n const cio = Math.max(ulo + DEPTH_MINUTE_FRACTIONS.floor, Math.round(minutes * DEPTH_MINUTE_FRACTIONS.cio));\n const sio = Math.max(cio + DEPTH_MINUTE_FRACTIONS.floor, 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 // Knowledge-Tree concepts are the spine of the course — the reconciler must\n // escalate them, never downgrade them. The emitter hardcoded `false`, so the\n // flag the schema documents never reached the planner for any concept.\n is_core: concept.is_core === true || str(concept.standardization) === 'standardized',\n external_prerequisites: [],\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 // A step with no id cannot become a node. Silently skipping it made a feature\n // with one such step look \"covered\" (steps.length > 0) while half of it — or\n // all of it — never reached the feed.\n if (!stepNode.id) {\n steplessStepIds.push(fid + '#' + i);\n continue;\n }\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 const knownConceptIds = new Set(conceptById.keys());\n\n // --- no phases -> legacy path (concepts first, then features) -----------\n if (rawPhases.length === 0) {\n const externalByNode = new Map<string, string[]>();\n for (const c of conceptRecs) {\n const node = feedNodeFromConcept(c);\n nodes.push(node);\n const { internal, external } = splitPrerequisites(c, knownConceptIds);\n if (external.length > 0) {\n node.external_prerequisites = external;\n externalByNode.set(node.id, external);\n }\n for (const p of internal) edges.push({ from: p, to: node.id, kind: 'knowledge', reason: 'concept prerequisite' });\n }\n noteExternalPrerequisites(opts, externalByNode);\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: buildSource(opts, 'hybrid_graph'),\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 /** concept id → the preview NODE id already emitted for it (always an earlier phase). */\n const previewNodeIdByConcept = new Map<string, string>();\n /** concept id → prerequisite ids that are not concepts in this analysis. */\n const externalByNode = new Map<string, 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(DEPTH_MINUTE_FRACTIONS.floor, Math.round(prevNode.estimated_minutes * DEPTH_MINUTE_FRACTIONS.preview));\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 previewNodeIdByConcept.set(cid, 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 // A prerequisite that is not a concept in THIS analysis is an entry\n // requirement, not an ordering problem. The old code blamed ordering for\n // it (\"neither introduced nor previewed in an EARLIER phase\") and threw,\n // which made any knowledge graph referencing prior knowledge un-emittable.\n if (!conceptById.has(p)) {\n if (!externalByNode.has(cid)) externalByNode.set(cid, []);\n externalByNode.get(cid)!.push(p);\n node.external_prerequisites.push(p);\n continue;\n }\n // A preview node in an EARLIER phase DOES satisfy a prerequisite — that\n // is precisely what it exists for (\"know it exists\" before the full\n // teach, so the dependent concept has the vocabulary). Requiring the\n // full anchor first made preview unusable for exactly the concepts that\n // need it most. A preview in the SAME phase does not count: within a\n // phase the sequence is new → advanced → preview, so it would arrive\n // after the dependent concept.\n const previewOfPrereq = previewNodeIdByConcept.get(p);\n if (!emittedConcepts.has(p) && !previewOfPrereq) {\n throw new Error('Concept ' + cid + ' (phase ' + pid + ') has prerequisite ' + p + ' that is neither introduced nor previewed in an EARLIER phase - fail-closed');\n }\n edges.push({ from: emittedConcepts.has(p) ? p : previewOfPrereq!, to: cid, kind: 'knowledge', reason: 'concept prerequisite' });\n }\n emittedConcepts.add(cid);\n conceptOrder.push(cid);\n } else if (str(intro.aspect, 'new') === 'new') {\n // auditPhasePlan rejects a duplicate `new`; the emitter is the last gate and\n // used to accept it silently, emitting no node for the repeat.\n throw new Error('Phase ' + pid + ' introduces ' + cid + ' as new but it was already introduced in an earlier phase - fail-closed');\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 // Reduce FIRST, then price the downgrade against the minutes the node\n // actually carries. It used to be computed on the pre-reduction value, so\n // `minutes_saved` referred to a number stored nowhere.\n node.estimated_minutes = Math.max(\n DEPTH_MINUTE_FRACTIONS.advancedFloor,\n Math.round(node.estimated_minutes * DEPTH_MINUTE_FRACTIONS.advancedRevisit),\n );\n node.depth_scaffold_candidates = [{\n from_depth: 'sio', to_depth: 'cio',\n minutes_saved: Math.round(node.estimated_minutes * DEPTH_MINUTE_FRACTIONS.advancedRecapSave),\n reason: 'advanced revisit may drop to CIO recap without breaking first-teach chain',\n }];\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 noteExternalPrerequisites(opts, externalByNode);\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 // Coverage the other way round: a concept the generator emitted but no phase\n // introduces is a hole in the course. The fail-closed check above only catches\n // concepts that HAVE a link — an unlinked concept used to vanish silently.\n const orphans = Array.from(conceptById.keys()).filter((cid) => !emittedConcepts.has(cid));\n if (orphans.length > 0) {\n pushDiagnostic(opts, 'orphan_concept', 'error',\n 'Knowledge graph has ' + orphans.length + ' concept(s) no phase introduces and no link consumes — they will not be taught: '\n + orphans.join(', '),\n orphans,\n );\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((x) => x.id === sid)!;\n node.phase_id = ctx.id;\n }\n ids.push(...stepIds);\n }\n if (ids.length === 0) {\n // FeedPhaseSchema requires at least one node; a phase that introduces and\n // owns nothing is not a teaching unit — surface it instead of letting Zod\n // throw \"Array must contain at least 1 element(s)\" with no phase name.\n throw new Error('Phase ' + ctx.id + ' contains no teaching nodes (no concepts introduced and no feature steps) - drop it or give it work - fail-closed');\n }\n phaseNodeIds.push(ids);\n }\n const idlessInHybrid = takeSteplessStepIds();\n if (idlessInHybrid.length > 0) {\n pushDiagnostic(opts, 'step_without_id', 'warning',\n 'Step(s) carrying no id cannot become planning nodes and were dropped (feature#index): ' + idlessInHybrid.join(', '),\n idlessInHybrid,\n );\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 pushDiagnostic(opts, 'hybrid_link_drift', 'warning',\n 'hybrid link drift: concept ' + cid + ' is fully taught in a later phase than consumer ' + firstStep + ' (a preview covers the dependency)',\n [cid, firstStep]);\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 = keywordKey(k);\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(keywordKey(k));\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 pushDiagnostic(opts,\n conceptOvertaken ? 'concept_overtaken_by_step' : 'keyword_order_drift',\n 'warning',\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 [src, n.id],\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: buildSource(opts, 'hybrid_graph'),\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 const externalByNode = new Map<string, string[]>();\n for (const c of arr(kg.concepts)) {\n const node = feedNodeFromConcept(c);\n nodes.push(node);\n const { internal, external } = splitPrerequisites(c, conceptIds);\n if (external.length > 0) {\n node.external_prerequisites = external;\n externalByNode.set(node.id, external);\n }\n for (const p of internal) {\n if (p !== node.id) {\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 noteExternalPrerequisites(opts, externalByNode);\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: buildSource(opts, 'knowledge_graph'),\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 const allFeatures = arr(pg.features);\n allFeatures.forEach((f, fi) => addFeatureGroup(f, str(f.id, 'F' + pad2(fi + 1)), nodes, edges, groupings));\n // A feature whose steps vanished (STEP_4_C2 failed for it) produces no node and\n // used to disappear with no trace. Surface it — the host decides whether a\n // partially-built project is acceptable.\n const stepless = allFeatures\n .filter((f) => arr(f.steps).length === 0)\n .map((f) => str(f.id))\n .filter(Boolean);\n if (stepless.length > 0) {\n pushDiagnostic(opts, 'feature_without_steps', 'warning',\n 'Feature(s) declared but carrying no steps — they will be absent from the feed: ' + stepless.join(', '),\n stepless,\n );\n }\n const idless = takeSteplessStepIds();\n if (idless.length > 0) {\n pushDiagnostic(opts, 'step_without_id', 'warning',\n 'Step(s) carrying no id cannot become planning nodes and were dropped (feature#index): ' + idless.join(', '),\n idless,\n );\n }\n if (nodes.length === 0 && allFeatures.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 ' + allFeatures.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: buildSource(opts, 'project_graph'),\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"]}
@@ -1,4 +1,4 @@
1
- import { L as LlmClientConfig } from './llmClient-ysPhLjcH.js';
1
+ import { L as LlmClientConfig, a as LlmClient } from './llmClient-CX5uUiQ1.js';
2
2
  import { u as ProjectGraph } from './projectGraphSchema-DnD7orZV.js';
3
3
 
4
4
  /**
@@ -19,6 +19,12 @@ interface ConceptMapping {
19
19
  keyword: string;
20
20
  depth: 'ulo' | 'cio' | 'sio';
21
21
  depth_rationale: string;
22
+ /**
23
+ * Who decided `depth`. The regex fallback (comment → ulo, import → sio) has no
24
+ * pedagogical basis — without this marker a degraded run is indistinguishable
25
+ * from a considered one, because both write the same field.
26
+ */
27
+ depth_source: 'llm' | 'heuristic' | 'override';
22
28
  evidence_files: string[];
23
29
  }
24
30
  interface EscalateOptions {
@@ -31,6 +37,9 @@ interface EscalateOptions {
31
37
  depth?: string;
32
38
  }>;
33
39
  llmConfig?: LlmClientConfig;
40
+ /** Pre-built client from the calling pipeline — reused so every step of one run
41
+ * lands in ONE callLog and the feed can report real provenance. */
42
+ sharedClient?: LlmClient;
34
43
  }
35
44
  /**
36
45
  * Escalate keywords/api_usage to neutral concepts with ULO/CIO/SIO depth and join evidence files.