@thanh01.pmt/domain-kit 0.2.1 → 0.4.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 (51) hide show
  1. package/README.md +42 -11
  2. package/dist/{chunk-CRN6D4HG.mjs → chunk-HILSUZOF.mjs} +32 -8
  3. package/dist/chunk-HILSUZOF.mjs.map +1 -0
  4. package/dist/{chunk-23TQPTHG.mjs → chunk-LZQLLAYP.mjs} +5 -5
  5. package/dist/{chunk-23TQPTHG.mjs.map → chunk-LZQLLAYP.mjs.map} +1 -1
  6. package/dist/{chunk-HH4UX65C.mjs → chunk-PKUEOSZ3.mjs} +42 -4
  7. package/dist/chunk-PKUEOSZ3.mjs.map +1 -0
  8. package/dist/{chunk-HUDFN4IX.mjs → chunk-QJF4QCBJ.mjs} +99 -4
  9. package/dist/chunk-QJF4QCBJ.mjs.map +1 -0
  10. package/dist/{chunk-WHBAGJWN.mjs → chunk-VCPIWJ7R.mjs} +4 -4
  11. package/dist/chunk-VCPIWJ7R.mjs.map +1 -0
  12. package/dist/curriculumFeedEmitter-6IK6PFDH.mjs +4 -0
  13. package/dist/{curriculumFeedEmitter-E5DAJYUZ.mjs.map → curriculumFeedEmitter-6IK6PFDH.mjs.map} +1 -1
  14. package/dist/{curriculumFeedSchema-TEeQg2bY.d.ts → curriculumFeedSchema-B66H3gEV.d.cts} +148 -8
  15. package/dist/{curriculumFeedSchema-TEeQg2bY.d.cts → curriculumFeedSchema-B66H3gEV.d.ts} +148 -8
  16. package/dist/feed/index.cjs +136 -3
  17. package/dist/feed/index.cjs.map +1 -1
  18. package/dist/feed/index.d.cts +1 -1
  19. package/dist/feed/index.d.ts +1 -1
  20. package/dist/feed/index.mjs +2 -2
  21. package/dist/graph/index.cjs +30 -5
  22. package/dist/graph/index.cjs.map +1 -1
  23. package/dist/graph/index.d.cts +2 -2
  24. package/dist/graph/index.d.ts +2 -2
  25. package/dist/graph/index.mjs +1 -1
  26. package/dist/{hybridGraphPipeline-DVZYLCAQ.d.ts → hybridGraphPipeline-DXQDbBEj.d.ts} +14 -2
  27. package/dist/{hybridGraphPipeline-DmWRKYXf.d.cts → hybridGraphPipeline-dT-bio-3.d.cts} +14 -2
  28. package/dist/{hybridGraphSchema-BCgXicgA.d.ts → hybridGraphSchema-nrUeSwpU.d.cts} +15 -15
  29. package/dist/{hybridGraphSchema-BCgXicgA.d.cts → hybridGraphSchema-nrUeSwpU.d.ts} +15 -15
  30. package/dist/index.cjs +168 -10
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +3 -3
  33. package/dist/index.d.ts +3 -3
  34. package/dist/index.mjs +5 -5
  35. package/dist/pipeline/index.cjs +165 -8
  36. package/dist/pipeline/index.cjs.map +1 -1
  37. package/dist/pipeline/index.d.cts +3 -3
  38. package/dist/pipeline/index.d.ts +3 -3
  39. package/dist/pipeline/index.mjs +4 -4
  40. package/dist/schemas/index.cjs +42 -4
  41. package/dist/schemas/index.cjs.map +1 -1
  42. package/dist/schemas/index.d.cts +2 -2
  43. package/dist/schemas/index.d.ts +2 -2
  44. package/dist/schemas/index.mjs +2 -2
  45. package/package.json +10 -9
  46. package/LICENSE +0 -21
  47. package/dist/chunk-CRN6D4HG.mjs.map +0 -1
  48. package/dist/chunk-HH4UX65C.mjs.map +0 -1
  49. package/dist/chunk-HUDFN4IX.mjs.map +0 -1
  50. package/dist/chunk-WHBAGJWN.mjs.map +0 -1
  51. package/dist/curriculumFeedEmitter-E5DAJYUZ.mjs +0 -4
package/README.md CHANGED
@@ -151,7 +151,8 @@ const feed = emitCurriculumFeed(result.projectGraph); // or knowledgeGraph or h
151
151
  console.log(feed.learning_nodes.length); // unified nodes ready for planner
152
152
  console.log(feed.dependency_edges.length); // knowledge + task edges
153
153
  console.log(feed.suggested_groupings); // feature/category groupings
154
- // Each node has: bloom_hint, depth_hint, keywords.new/.prerequisite
154
+ console.log(feed.phases.length); // non-empty ⇒ learning_nodes are in teaching order (phases define units)
155
+ // Each node has: bloom_hint, depth_hint, keywords.new/.prerequisite, introduce_aspect, user_visible_deliverable
155
156
  ```
156
157
 
157
158
  ---
@@ -399,10 +400,11 @@ All schemas are Zod-validated TypeScript types.
399
400
  | `DomainProfileSchema` | `domainProfileSchema.ts` | Domain detection output |
400
401
  | `HardwareFeatureExtension` | `domainExtensions.ts` | Hardware overlay (wiring, materials) |
401
402
  | `ThreeDesignFeatureExtension` | `domainExtensions.ts` | 3D Design overlay (shapes, dimensions) |
402
- | `CurriculumFeedSchema` | `curriculumFeedSchema.ts` | Planner-ready normalized feed (nodes, edges, groupings) |
403
+ | `CurriculumFeedSchema` | `curriculumFeedSchema.ts` | Planner-ready normalized feed (nodes, edges, groupings, phases) |
403
404
  | `FeedNodeSchema` | `curriculumFeedSchema.ts` | Individual learning node (concept, skill, or product step) |
404
405
  | `FeedEdgeSchema` | `curriculumFeedSchema.ts` | Dependency edge (knowledge or task) |
405
406
  | `FeedGroupingSchema` | `curriculumFeedSchema.ts` | Suggested grouping of nodes (feature or category) |
407
+ | `FeedPhaseSchema` | `curriculumFeedSchema.ts` | Development phase — when non-empty, defines teaching sequence and unit structure |
406
408
 
407
409
  ### Key Types
408
410
 
@@ -486,9 +488,10 @@ type AssembledRoadmap = {
486
488
  type CurriculumFeed = {
487
489
  schema_version: 2;
488
490
  source: { graph_type: 'project_graph' | 'knowledge_graph' | 'hybrid_graph'; warnings: string[]; hallucination_count: number };
489
- learning_nodes: FeedNode[];
491
+ learning_nodes: FeedNode[]; // in TEACHING ORDER when phases[] is non-empty
490
492
  dependency_edges: FeedEdge[];
491
493
  suggested_groupings: FeedGrouping[];
494
+ phases: FeedPhase[]; // when non-empty, THESE — not suggested_groupings — define the teaching sequence and unit structure
492
495
  }
493
496
 
494
497
  type FeedNode = {
@@ -505,6 +508,20 @@ type FeedNode = {
505
508
  references: { file: string; evidence: string }[];
506
509
  feature_id: string | null;
507
510
  concept_ref: string | null;
511
+ phase_id: string; // development phase that introduces/uses this node ('' on legacy feeds)
512
+ introduce_aspect: 'new' | 'advanced' | 'preview' | null; // first intro / advanced revisit / preview priming
513
+ user_visible_deliverable: string; // step's outcome.user_visible — checkpoint candidate for session cutting ('' on concept nodes)
514
+ depth_variants?: { ulo: number; cio: number; sio: number }; // minutes per depth level — reconciler price list (ULO ≤ CIO ≤ SIO)
515
+ depth_scaffold_candidates?: { from_depth: 'ulo' | 'cio' | 'sio'; to_depth: 'ulo' | 'cio' | 'sio'; minutes_saved: number; reason: string }[]; // DEPTH downgrades (SIO→CIO, CIO→ULO) — parallel to scaffold_candidates
516
+ is_core: boolean; // Master-Tree core concept — never depth-downgraded; escalate instead
517
+ }
518
+
519
+ type FeedPhase = {
520
+ id: string;
521
+ order: number; // 1-based teaching order
522
+ name: string;
523
+ product_completion: string;
524
+ node_ids: string[]; // nodes of this phase, already in teaching order
508
525
  }
509
526
 
510
527
  type FeedEdge = {
@@ -640,19 +657,26 @@ const feed = emitCurriculumFeed(graph, {
640
657
  });
641
658
 
642
659
  // Returns CurriculumFeed with:
643
- // - learning_nodes[] — unified node format (concept | skill | product_step)
660
+ // - learning_nodes[] — unified node format (concept | skill | product_step), in teaching order when phases[] is non-empty
644
661
  // - dependency_edges[] — knowledge (prerequisite) or task (build order) edges
645
- // - suggested_groupings[] — feature or category groupings
646
- // - Each node has: bloom_hint, depth_hint (ulo/cio/sio), keywords.new vs .prerequisite
662
+ // - suggested_groupings[] — feature or category groupings (layout hint only)
663
+ // - phases[] — development phases; when non-empty, the teaching sequence + unit structure
664
+ // - Each node has: bloom_hint, depth_hint (ulo/cio/sio), keywords.new vs .prerequisite,
665
+ // introduce_aspect (new/advanced/preview), user_visible_deliverable,
666
+ // depth_variants + depth_scaffold_candidates + is_core (depth-reconcile inputs)
647
667
  ```
648
668
 
649
669
  **Key behaviors:**
650
- - **Hybrid graphs**: concepts emitted first (prerequisite-first), then project steps; hybrid links carry depth hint onto step nodes; `estimated_prereq_minutes` accumulated per concept node
670
+ - **Teaching-order emission (contract, 2026-09-18):** when a hybrid graph carries `phases[]`, the `nodes` array follows the **phase teaching sequence** — the order in the file IS the order in the classroom. The former "concepts emitted first, then steps" array layout is ABOLISHED: it contradicted phase order, fabricated backwards prerequisite edges, and caused the planner's "Teaching-order violation" failure (see postmortem 2026-09-17 §9)
671
+ - **No fabricated backwards edges:** when a keyword consumer sits in an EARLIER phase than the concept's anchor teach (and no preview covers it), the emitter does NOT invent a dependency edge — it records a drift **warning** instead (`hybrid link drift: …`); the host decides, the emitter reports
672
+ - **Overtake guard:** a product step must never become the introducer of a concept's vocabulary; the first appearance of a keyword belongs to the concept (or its preview node) that teaches it
673
+ - **Preview priming (aspect `preview`, kit ≥0.3.0):** a phase may declare a light awareness pass for a concept whose anchor teach lives in a LATER phase — emitted as node `<cid>__PREVxx` with depth `ulo`, bloom `Understand`, ~25% minutes (min 5), `keywords.all` (making it the legitimate vocabulary introducer), no prerequisite edges, no scaffolding. Five fail-closed emission rules: unknown concept, already-introduced concept, duplicate preview, no anchor anywhere, anchor not in a later phase
651
674
  - **Knowledge graphs**: concepts + prerequisite edges + category groupings
652
675
  - **Project graphs**: features → steps + depends_on edges + feature groupings
653
676
  - **Pass-through**: if input already has `learning_nodes[]`, validates and returns as-is
654
677
  - **Legacy rejection**: throws descriptive error if graph has `implementation.tasks` but no `features[].steps` (old knowledge-tree format)
655
- - **New/prerequisite keywords**: computed in emission order — first appearance = new, seen before = prerequisite
678
+ - **New/prerequisite keywords**: computed in **teaching order** (phase sequence when phases exist, array order otherwise) — first appearance = new, seen before = prerequisite. Anchoring keywords to array position instead of teaching order is the exact assumption that caused the 2026-09-17 incident
679
+ - **Fail-closed validation**: output is validated before return — duplicate IDs, unresolvable edges, self-loops, non-empty, **and the teaching-order invariant**: when phases define the sequence, a prerequisite must live in the same or an EARLIER phase than its consumer (a violation throws at emission, never defers to the planner)
656
680
 
657
681
  ### Parsers
658
682
 
@@ -723,8 +747,15 @@ STEP 9: Feed Emission
723
747
  → Convert project_graph → FeedNode[] (kind: product_step) + FeatureGroupings
724
748
  → Convert knowledge_graph → FeedNode[] (kind: concept) + CategoryGroupings
725
749
  → Convert hybrid_graph → combined nodes + knowledge/task edges + hybrid link depth hints
726
- → Compute new/prerequisite keywords in emission order
727
- → Validate: fail-closed (duplicate IDs, edge resolution, self-loops, non-empty)
750
+ → Project steps: project outcome.user_visible onto user_visible_deliverable (checkpoint candidate)
751
+ → Concept nodes: emit depth_variants (ULO ≤ CIO ≤ SIO minutes) + depth_scaffold_candidates + is_core
752
+ → Teaching order: when phases[] exist, nodes are emitted in phase sequence; new/prerequisite
753
+ keywords computed in TEACHING order (first appearance = new), never array position
754
+ → Preview priming: phase-declared __PREVxx awareness nodes carry keywords.all (legitimate
755
+ vocabulary introducer) without fabricating prerequisite edges
756
+ → Validate: fail-closed (duplicate IDs, edge resolution, self-loops, non-empty, duplicate
757
+ phase ids, and the teaching-order invariant: a prerequisite must live in the same or an
758
+ EARLIER phase than its consumer — a backwards edge throws at emission, never in the planner)
728
759
  ```
729
760
 
730
761
  ### Knowledge Graph Pipeline (Concept-Driven)
@@ -949,7 +980,7 @@ import {
949
980
  ProjectGraphSchema, KnowledgeGraphSchema, HybridGraphSchema,
950
981
  DomainProfileSchema, ConceptMappingSchema, AssembledRoadmapSchema,
951
982
  KnowledgeConceptSchema, KnowledgeLearningPathStepSchema,
952
- CurriculumFeedSchema, FeedNodeSchema, FeedEdgeSchema, FeedGroupingSchema,
983
+ CurriculumFeedSchema, FeedNodeSchema, FeedEdgeSchema, FeedGroupingSchema, FeedPhaseSchema,
953
984
  } from '@thanh01.pmt/domain-kit';
954
985
 
955
986
  // Roadmap assembly
@@ -2120,6 +2120,8 @@ function auditPhasePlan(plan, features, concepts, links, warnings, log) {
2120
2120
  const phases = [];
2121
2121
  const featurePhase = /* @__PURE__ */ new Map();
2122
2122
  const newIntroPhase = /* @__PURE__ */ new Map();
2123
+ const previewsSeen = /* @__PURE__ */ new Set();
2124
+ const previewPhase = /* @__PURE__ */ new Map();
2123
2125
  const errors = [];
2124
2126
  plan.forEach((rp, idx) => {
2125
2127
  const id = String(rp.id || "PH" + (idx + 1));
@@ -2151,7 +2153,22 @@ function auditPhasePlan(plan, features, concepts, links, warnings, log) {
2151
2153
  errors.push("Phase " + id + ": introduces unknown concept " + cid);
2152
2154
  continue;
2153
2155
  }
2154
- const aspect = String(ri.aspect || "new") === "advanced" ? "advanced" : "new";
2156
+ const rawAspect = String(ri.aspect || "new");
2157
+ const aspect = rawAspect === "advanced" || rawAspect === "preview" ? rawAspect : "new";
2158
+ if (aspect === "preview") {
2159
+ if (newIntroPhase.has(cid)) {
2160
+ errors.push("Phase " + id + ": preview of " + cid + " after its new introduction in phase " + newIntroPhase.get(cid) + " - preview must precede the anchor teach");
2161
+ continue;
2162
+ }
2163
+ if (previewsSeen.has(cid)) {
2164
+ errors.push("Phase " + id + ": duplicate preview of " + cid);
2165
+ continue;
2166
+ }
2167
+ previewsSeen.add(cid);
2168
+ previewPhase.set(cid, order);
2169
+ introductions.push({ concept_id: cid, aspect, note: String(ri.note || "") });
2170
+ continue;
2171
+ }
2155
2172
  if (aspect === "advanced") {
2156
2173
  const first = newIntroPhase.get(cid);
2157
2174
  if (first === void 0) {
@@ -2199,10 +2216,17 @@ function auditPhasePlan(plan, features, concepts, links, warnings, log) {
2199
2216
  }
2200
2217
  for (const [cid, consumerOrder] of firstConsumerPhase) {
2201
2218
  const intro = newIntroPhase.get(cid);
2202
- if (intro === void 0) {
2219
+ const prev = previewPhase.get(cid);
2220
+ if (intro === void 0 && prev === void 0) {
2203
2221
  errors.push("Concept " + cid + " is consumed from phase " + consumerOrder + " but never introduced in any phase");
2204
- } else if (intro > consumerOrder) {
2205
- errors.push("Concept " + cid + " introduced in phase " + intro + " but first consumed in phase " + consumerOrder + " - introduction must come no later than first consumption");
2222
+ } else if (intro !== void 0 && intro > consumerOrder && (prev === void 0 || prev > consumerOrder)) {
2223
+ errors.push("Concept " + cid + " introduced in phase " + intro + " but first consumed in phase " + consumerOrder + " - introduction must come no later than first consumption (or a preview must cover the dependency)");
2224
+ }
2225
+ }
2226
+ for (const [cid, pOrder] of previewPhase) {
2227
+ const anchor = newIntroPhase.get(cid);
2228
+ if (anchor !== void 0 && anchor <= pOrder) {
2229
+ errors.push("Concept " + cid + " previewed in phase " + pOrder + " but fully introduced in phase " + anchor + " - preview must precede the anchor introduction");
2206
2230
  }
2207
2231
  }
2208
2232
  return { phases, errors };
@@ -2218,7 +2242,7 @@ async function decomposePhases(args) {
2218
2242
  const linkList = links.map(
2219
2243
  (l) => "- " + l.feature_id + " -> " + l.concept_id + " (" + l.depth + ")"
2220
2244
  ).join("\n") || "(none)";
2221
- 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.\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.";
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.';
2222
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";
2223
2247
  const MAX_DECOMPOSE_ATTEMPTS = 3;
2224
2248
  let auditErrors = [];
@@ -2256,6 +2280,6 @@ async function decomposePhases(args) {
2256
2280
  return phases;
2257
2281
  }
2258
2282
 
2259
- 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 };
2260
- //# sourceMappingURL=chunk-CRN6D4HG.mjs.map
2261
- //# sourceMappingURL=chunk-CRN6D4HG.mjs.map
2283
+ 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 };
2284
+ //# sourceMappingURL=chunk-HILSUZOF.mjs.map
2285
+ //# sourceMappingURL=chunk-HILSUZOF.mjs.map