@thanh01.pmt/curriculum-kit 1.4.47 → 1.4.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,3 +20,26 @@ A high-fidelity AI curriculum and pedagogical content generation engine powered
20
20
  ## 🌐 Language Policy
21
21
  - **100% English Canonical Prompts:** All system instructions and prompt builders are written strictly in English.
22
22
  - **Strict Target Output Language:** Every generation flow accepts `language` (`"vi"`, `"en"`, etc.), and the AI strictly outputs pedagogical explanations in the requested language while preserving international technical syntax and keywords.
23
+
24
+ ## 🧮 Planner Internals (P52, 2026-09-18)
25
+
26
+ `buildCurriculumPlan` turns a `CurriculumFeed` into a session plan in three ordered stages. All three are pure functions of the feed — no LLM calls.
27
+
28
+ ### 1. Depth reconcile — `reconcilePlanDepth` (services/depthReconciler.ts)
29
+
30
+ Runs **before** unit building/packing, only when `total_sessions` is fixed. When the bottom-up minute total exceeds the course budget (`total_sessions × session minutes × (1 − overhead_ratio)`), the planner previously had only one direction: stretch time (part-sessions). The reconciler adds the missing one — **lower knowledge depth along the Bruner spiral (SIO → CIO → ULO)** instead of cutting phases.
31
+
32
+ - **Downgrade priority (least pedagogical loss first):** (1) `advanced` revisit nodes (`__ADVxx` ids), (2) SIO→CIO on leaf concepts, (3) SIO→CIO on non-leaf non-core, (4) CIO→ULO on leaf, (5) CIO→ULO on non-leaf non-core; within a tier, largest `minutes_saved` first. Leaf-ness is computed at the **concept level** (`classifyDepthCandidates`: a concept is a leaf when no other concept lists it as a prerequisite; `__ADV`/`__PREV` ids map back to their base).
33
+ - **Absolute exclusions:** `is_core` concepts (Master-Tree core — escalate instead), non-concept nodes (product steps are build work, not knowledge depth), and nodes with **no declared `depth_hint`** (never guess).
34
+ - **Bound:** fixed-point loop capped at 5 rounds; mutates only `minutes`/`depth_hint` — no edges added/removed, no order changes, so the teaching-order invariant is preserved by construction.
35
+ - **Escalation:** when candidates are exhausted while still over budget, returns `escalate: true` + `deficitMinutes`; the planner surfaces a `budget_escalation: … human review required` warning — it **never silently cuts a phase**.
36
+
37
+ ### 2. Unit building + checkpoint-aware session cutting — `packUnitSessions`
38
+
39
+ Bins nodes into sessions at the content budget. When the next node would overflow, `cutBackToCheckpoint` searches for the **latest node whose cumulative minutes fall inside the window `[budget × (1 − SESSION_CUT_TOLERANCE), budget]`** (`SESSION_CUT_TOLERANCE = 0.15`, constants/pedagogy.ts) that carries a **`user_visible_deliverable`** (projected from the ProjectGraph step's `outcome.user_visible` by domain-kit). The cut lands on that checkpoint; displaced nodes are requeued in teaching order (nothing is dropped, wall-clock never exceeds the session length). Oversized nodes keep the existing multi-part split. **No checkpoint inside the window → raw minute cut + a `no-checkpoint-within-tolerance` warning** (the future hook for `scaffold_candidates`).
40
+
41
+ ### 3. ZPD audit
42
+
43
+ The single ZPD/overload audit pass runs **after** reconcile and packing, so it always sees the final node set — the reconciler cannot invalidate it by construction (it only shrinks minutes/depth, never adds new-concept events).
44
+
45
+ Planning nodes carry the reconcile inputs from the feed: `depth_variants` ({ulo, cio, sio} minute estimates), `depth_scaffold_candidates`, and `is_core`. See the domain-kit README (Schemas → `FeedNode`) for the feed-side contract.
package/dist/ai/index.cjs CHANGED
@@ -1845,9 +1845,9 @@ Evaluate the candidate artifact across these 6 dimensions:
1845
1845
  - 0 pts: Missing critical sections.
1846
1846
 
1847
1847
  5. **Technical & Conceptual Accuracy (15 pts):**
1848
- - 15 pts: Code snippets, logic flows, architectural descriptions, and Mermaid diagrams are conceptually sound and valid. If \`canonicalExposition\` or \`[KNOWLEDGE_EXPOSITION]\` is provided, content strictly adheres to canonical concepts without contradicting definitions or examples.
1848
+ - 15 pts: Code snippets, logic flows, architectural descriptions, and Mermaid diagrams are conceptually sound and valid. Every code identifier uses the REAL standard-library/API spelling of the declared stack (grounded in REFERENCE_PACK / canonical LESSON) \u2014 internal consistency with a glossary term name is NOT evidence of correctness. If \`canonicalExposition\` or \`[KNOWLEDGE_EXPOSITION]\` is provided, content strictly adheres to canonical concepts without contradicting definitions or examples.
1849
1849
  - 8 pts: Minor syntax inaccuracies or unidiomatic code that does not break core concepts.
1850
- - 0 pts: Severe technical hallucinations, broken logic, invalid diagrams, or direct contradiction of canonical exposition knowledge.
1850
+ - 0 pts: Severe technical hallucinations, broken logic, invalid diagrams, invented casing/spelling rules for standard-library identifiers, or direct contradiction of canonical exposition knowledge.
1851
1851
 
1852
1852
  6. **Contract Consistency & Zero-Drift (10 pts):**
1853
1853
  - 10 pts: Satellite artifact strictly adheres to the scope, tech stack, and objectives of Lesson ${lessonId} without introducing unrelated topics. If \`canonicalLessonExcerpt\` is present in candidate JSON, all satellite phases/activities strictly operationalize the master Lesson Flow and Activity Sequence without inventing divergent timelines.
@@ -3727,7 +3727,25 @@ var PlanningNodeSchema = zod.z.object({
3727
3727
  file: zod.z.string(),
3728
3728
  evidence: zod.z.string().default("")
3729
3729
  })).default([]),
3730
- phase_id: zod.z.string().default("")
3730
+ phase_id: zod.z.string().default(""),
3731
+ // P52/T2.1: tangible user-visible outcome projected from the graph step
3732
+ // (checkpoint candidate for session cutting). Empty = not a checkpoint.
3733
+ user_visible_deliverable: zod.z.string().default(""),
3734
+ // P52/T3.1 — depth-reconcile inputs (mirrored from the feed; optional so
3735
+ // raw graphs without variants keep planning unchanged):
3736
+ // • depth_variants: minutes to teach this node at each depth level (ULO ≤
3737
+ // CIO ≤ SIO) — the reconciler's price list for downgrades.
3738
+ // • depth_scaffold_candidates: parallel to scaffold_candidates but acting
3739
+ // on DEPTH (SIO→CIO, CIO→ULO) instead of lesson time.
3740
+ // • is_core: Master-Tree core concept — never downgraded; escalate instead.
3741
+ depth_variants: zod.z.object({ ulo: zod.z.number().int().nonnegative(), cio: zod.z.number().int().nonnegative(), sio: zod.z.number().int().nonnegative() }).optional(),
3742
+ depth_scaffold_candidates: zod.z.array(zod.z.object({
3743
+ from_depth: DepthLevelSchema,
3744
+ to_depth: DepthLevelSchema,
3745
+ minutes_saved: zod.z.number().int().nonnegative(),
3746
+ reason: zod.z.string().default("")
3747
+ })).optional(),
3748
+ is_core: zod.z.boolean().default(false)
3731
3749
  });
3732
3750
  var DependencyEdgeSchema = zod.z.object({
3733
3751
  from: zod.z.string(),