@thanh01.pmt/curriculum-kit 1.4.22 → 1.4.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +189 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.mjs +189 -4
- package/dist/index.mjs.map +1 -1
- package/dist/workflow/index.cjs.map +1 -1
- package/dist/workflow/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -165,6 +165,20 @@ interface ProduceLessonOptions {
|
|
|
165
165
|
mode?: 'search_only' | 'gen_only' | 'auto';
|
|
166
166
|
};
|
|
167
167
|
approvedArtifacts?: string[];
|
|
168
|
+
/**
|
|
169
|
+
* 5-Whys RC-canonicality: project briefing feeding the canonical PRODUCT_SPEC
|
|
170
|
+
* block (platform, hardware, deliverable template). Injected identically into
|
|
171
|
+
* every artifact's ground truth so no artifact re-decides product shape.
|
|
172
|
+
*/
|
|
173
|
+
briefing?: {
|
|
174
|
+
techStack?: string;
|
|
175
|
+
coreTechnology?: string;
|
|
176
|
+
topic?: string;
|
|
177
|
+
hardwarePlatform?: string;
|
|
178
|
+
studentEquipment?: string;
|
|
179
|
+
classDynamic?: string;
|
|
180
|
+
language?: string;
|
|
181
|
+
};
|
|
168
182
|
/**
|
|
169
183
|
* P46 RC2: host-provided inference override (Vercel AI SDK gateway).
|
|
170
184
|
* When supplied, ALL lesson/satellite LLM calls route through it — the kit's
|
|
@@ -658,6 +672,12 @@ interface GenerateExpositionOptions {
|
|
|
658
672
|
/** REFERENCE_PACK.md raw content — technical ground truth (versions, APIs).
|
|
659
673
|
* 5-Whys RC-vacuum: without it the KX model regresses to pretrained facts. */
|
|
660
674
|
refPack?: string;
|
|
675
|
+
/** 5-Whys RC-canonicality: canonical PRODUCT_SPEC fields so KX does not
|
|
676
|
+
* re-decide the deliverable shape that LESSON/satellites already share. */
|
|
677
|
+
productSpec?: {
|
|
678
|
+
deliverableTemplate?: string;
|
|
679
|
+
primaryLanguage?: string;
|
|
680
|
+
};
|
|
661
681
|
}
|
|
662
682
|
interface ExpositionResult {
|
|
663
683
|
relPath: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -165,6 +165,20 @@ interface ProduceLessonOptions {
|
|
|
165
165
|
mode?: 'search_only' | 'gen_only' | 'auto';
|
|
166
166
|
};
|
|
167
167
|
approvedArtifacts?: string[];
|
|
168
|
+
/**
|
|
169
|
+
* 5-Whys RC-canonicality: project briefing feeding the canonical PRODUCT_SPEC
|
|
170
|
+
* block (platform, hardware, deliverable template). Injected identically into
|
|
171
|
+
* every artifact's ground truth so no artifact re-decides product shape.
|
|
172
|
+
*/
|
|
173
|
+
briefing?: {
|
|
174
|
+
techStack?: string;
|
|
175
|
+
coreTechnology?: string;
|
|
176
|
+
topic?: string;
|
|
177
|
+
hardwarePlatform?: string;
|
|
178
|
+
studentEquipment?: string;
|
|
179
|
+
classDynamic?: string;
|
|
180
|
+
language?: string;
|
|
181
|
+
};
|
|
168
182
|
/**
|
|
169
183
|
* P46 RC2: host-provided inference override (Vercel AI SDK gateway).
|
|
170
184
|
* When supplied, ALL lesson/satellite LLM calls route through it — the kit's
|
|
@@ -658,6 +672,12 @@ interface GenerateExpositionOptions {
|
|
|
658
672
|
/** REFERENCE_PACK.md raw content — technical ground truth (versions, APIs).
|
|
659
673
|
* 5-Whys RC-vacuum: without it the KX model regresses to pretrained facts. */
|
|
660
674
|
refPack?: string;
|
|
675
|
+
/** 5-Whys RC-canonicality: canonical PRODUCT_SPEC fields so KX does not
|
|
676
|
+
* re-decide the deliverable shape that LESSON/satellites already share. */
|
|
677
|
+
productSpec?: {
|
|
678
|
+
deliverableTemplate?: string;
|
|
679
|
+
primaryLanguage?: string;
|
|
680
|
+
};
|
|
661
681
|
}
|
|
662
682
|
interface ExpositionResult {
|
|
663
683
|
relPath: string;
|
package/dist/index.mjs
CHANGED
|
@@ -11612,6 +11612,137 @@ function extractSymbolLedger(lessonMarkdown) {
|
|
|
11612
11612
|
return result;
|
|
11613
11613
|
}
|
|
11614
11614
|
|
|
11615
|
+
// src/services/artifactValidators.ts
|
|
11616
|
+
var CJK_RE = /[\u3400-\u4DBF\u4E00-\u9FFF\u3040-\u30FF\uAC00-\uD7AF]/g;
|
|
11617
|
+
function scanCjkLeaks(content) {
|
|
11618
|
+
const matches = content.match(CJK_RE);
|
|
11619
|
+
if (!matches || matches.length === 0) return null;
|
|
11620
|
+
const lines = [];
|
|
11621
|
+
for (const line of content.split("\n")) {
|
|
11622
|
+
if (CJK_RE.test(line)) lines.push(line.trim().slice(0, 160));
|
|
11623
|
+
}
|
|
11624
|
+
return {
|
|
11625
|
+
code: "cjk-leak",
|
|
11626
|
+
detail: `Ph\xE1t hi\u1EC7n ${matches.length} k\xFD t\u1EF1 CJK (Trung/Nh\u1EADt/H\xE0n) trong artifact ng\xF4n ng\u1EEF Vi\u1EC7t/Anh. B\u1ECB c\u1EA5m tuy\u1EC7t \u0111\u1ED1i.`,
|
|
11627
|
+
evidence: lines.slice(0, 5)
|
|
11628
|
+
};
|
|
11629
|
+
}
|
|
11630
|
+
var VERSION_CLAIM_RE = /\b([A-Z][A-Za-z+#.\-]{1,24})\s+(\d{1,2}(?:\.\d{1,2})?)\b/g;
|
|
11631
|
+
function checkVersionGroundTruth(content, refPack) {
|
|
11632
|
+
if (!refPack || !refPack.trim()) return null;
|
|
11633
|
+
const packLower = refPack.toLowerCase();
|
|
11634
|
+
const claims = /* @__PURE__ */ new Map();
|
|
11635
|
+
for (const m of content.matchAll(VERSION_CLAIM_RE)) {
|
|
11636
|
+
const tool = m[1];
|
|
11637
|
+
if (claims.size === 0 || !claims.has(tool)) claims.set(tool, m[2]);
|
|
11638
|
+
}
|
|
11639
|
+
const contradictions = [];
|
|
11640
|
+
for (const [tool, claimed] of claims) {
|
|
11641
|
+
const toolRe = new RegExp(`\\b${tool.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s+(\\d{1,2}(?:\\.\\d{1,2})?)\\b`, "i");
|
|
11642
|
+
const packMatch = packLower.match(toolRe);
|
|
11643
|
+
if (packMatch && packMatch[1] !== claimed) {
|
|
11644
|
+
contradictions.push(`${tool}: artifact n\xF3i ${claimed}, REFERENCE_PACK ch\u1ED1t ${packMatch[1]}`);
|
|
11645
|
+
}
|
|
11646
|
+
}
|
|
11647
|
+
if (contradictions.length === 0) return null;
|
|
11648
|
+
return {
|
|
11649
|
+
code: "version-contradiction",
|
|
11650
|
+
detail: `Version m\xE2u thu\u1EABn v\u1EDBi ground truth REFERENCE_PACK: ${contradictions.join("; ")}. S\u1EEDa theo b\u1EA3n trong REFERENCE_PACK.`,
|
|
11651
|
+
evidence: contradictions
|
|
11652
|
+
};
|
|
11653
|
+
}
|
|
11654
|
+
function checkScopeDrift(content, scopedKeywords, tolerance = 0) {
|
|
11655
|
+
const conceptHeaders = /* @__PURE__ */ new Set();
|
|
11656
|
+
for (const m of content.matchAll(/\*\*([^*\n]{3,60}?)\s*\((?:WHAT|CIO|SIO|ULO)[^)]*\):\*\*/g)) {
|
|
11657
|
+
conceptHeaders.add(m[1].toLowerCase());
|
|
11658
|
+
}
|
|
11659
|
+
if (conceptHeaders.size === 0) return null;
|
|
11660
|
+
const drift = [];
|
|
11661
|
+
for (const header of conceptHeaders) {
|
|
11662
|
+
const hit = scopedKeywords.some((kw) => {
|
|
11663
|
+
const k = kw.toLowerCase().trim();
|
|
11664
|
+
if (!k) return false;
|
|
11665
|
+
const tokens = k.split(/\s+/);
|
|
11666
|
+
const present = tokens.filter((t) => header.includes(t)).length;
|
|
11667
|
+
return present >= Math.max(1, tokens.length - tolerance);
|
|
11668
|
+
});
|
|
11669
|
+
if (!hit) drift.push(header);
|
|
11670
|
+
}
|
|
11671
|
+
if (drift.length === 0) return null;
|
|
11672
|
+
return {
|
|
11673
|
+
code: "scope-drift",
|
|
11674
|
+
detail: `C\xE1c kh\xE1i ni\u1EC7m sau KH\xD4NG thu\u1ED9c scope c\u1EE7a bu\u1ED5i h\u1ECDc (kh\xF4ng c\xF3 trong new_keywords/graph node keywords): ${drift.join(" | ")}. Xo\xE1 ho\xE0n to\xE0n ho\u1EB7c thay b\u1EB1ng kh\xE1i ni\u1EC7m trong scope.`,
|
|
11675
|
+
evidence: drift
|
|
11676
|
+
};
|
|
11677
|
+
}
|
|
11678
|
+
function validateArtifactDeterministic(input) {
|
|
11679
|
+
const issues = [];
|
|
11680
|
+
const cjk = scanCjkLeaks(input.content);
|
|
11681
|
+
if (cjk) issues.push(cjk);
|
|
11682
|
+
if (input.refPack) {
|
|
11683
|
+
const ver = checkVersionGroundTruth(input.content, input.refPack);
|
|
11684
|
+
if (ver) issues.push(ver);
|
|
11685
|
+
}
|
|
11686
|
+
if (input.scopedKeywords && input.scopedKeywords.length > 0) {
|
|
11687
|
+
const scope = checkScopeDrift(input.content, input.scopedKeywords);
|
|
11688
|
+
if (scope) issues.push(scope);
|
|
11689
|
+
}
|
|
11690
|
+
return { ok: issues.length === 0, issues };
|
|
11691
|
+
}
|
|
11692
|
+
function validatorRepairPrompt(issues) {
|
|
11693
|
+
return [
|
|
11694
|
+
"DETERMINISTIC VALIDATOR FAILURES (mechanically detected \u2014 non-negotiable, fix ALL):",
|
|
11695
|
+
...issues.map((i, n) => `${n + 1}. [${i.code}] ${i.detail}${i.evidence.length ? `
|
|
11696
|
+
Evidence: ${i.evidence.join(" | ").slice(0, 400)}` : ""}`)
|
|
11697
|
+
].join("\n");
|
|
11698
|
+
}
|
|
11699
|
+
|
|
11700
|
+
// src/services/productSpec.ts
|
|
11701
|
+
function extractToolchainFacts(refPack) {
|
|
11702
|
+
const { excerpt } = buildSectionAwareExcerpt(refPack, {
|
|
11703
|
+
priorities: ["Technical Overview & Architecture Blueprint", "Hardware Pinout & Wiring Configuration Matrix"],
|
|
11704
|
+
budget: 4e3
|
|
11705
|
+
});
|
|
11706
|
+
if (!excerpt) return [];
|
|
11707
|
+
const factLines = [];
|
|
11708
|
+
for (const rawLine of excerpt.split("\n")) {
|
|
11709
|
+
const line = rawLine.trim();
|
|
11710
|
+
if (!line || line.startsWith("#")) continue;
|
|
11711
|
+
if (/\d{1,2}(?:\.\d{1,2})?/.test(line) || /Xcode|Swift|macOS|iOS|SDK|Node|Python|JDK|\.NET/i.test(line)) {
|
|
11712
|
+
factLines.push(line.replace(/^[-*•]\s*/, "- "));
|
|
11713
|
+
}
|
|
11714
|
+
if (factLines.length >= 8) break;
|
|
11715
|
+
}
|
|
11716
|
+
return factLines;
|
|
11717
|
+
}
|
|
11718
|
+
function buildProductSpecBlock(input) {
|
|
11719
|
+
const b = input.briefing || {};
|
|
11720
|
+
const platform = (input.overrides?.primaryLanguage || b.coreTechnology || b.techStack || b.topic || "").trim();
|
|
11721
|
+
const hardware = (b.hardwarePlatform || b.studentEquipment || b.classDynamic || "").trim();
|
|
11722
|
+
const toolchainFacts = input.refPack ? extractToolchainFacts(input.refPack) : [];
|
|
11723
|
+
const lines = [];
|
|
11724
|
+
lines.push("[PRODUCT SPEC \u2014 CANONICAL, BINDING FOR EVERY ARTIFACT OF THIS PROJECT]:");
|
|
11725
|
+
lines.push("All decisions below are PROJECT-WIDE. Every artifact (LESSON, satellites, KX)");
|
|
11726
|
+
lines.push("must describe the SAME product. Contradicting or re-deciding any line here is a");
|
|
11727
|
+
lines.push("consistency failure \u2014 if a needed decision is absent, stay GENERIC, never invent.");
|
|
11728
|
+
lines.push("");
|
|
11729
|
+
if (platform) {
|
|
11730
|
+
lines.push(`- Primary technology: ${platform}`);
|
|
11731
|
+
}
|
|
11732
|
+
if (hardware) {
|
|
11733
|
+
lines.push(`- Hardware / classroom setup: ${hardware}`);
|
|
11734
|
+
}
|
|
11735
|
+
if (input.overrides?.deliverableTemplate) {
|
|
11736
|
+
lines.push(`- Deliverable template: ${input.overrides.deliverableTemplate}`);
|
|
11737
|
+
}
|
|
11738
|
+
if (toolchainFacts.length > 0) {
|
|
11739
|
+
lines.push("- Toolchain & versions (ground truth):");
|
|
11740
|
+
for (const f of toolchainFacts) lines.push(` ${f}`);
|
|
11741
|
+
}
|
|
11742
|
+
if (lines.length <= 5) return "";
|
|
11743
|
+
return lines.join("\n");
|
|
11744
|
+
}
|
|
11745
|
+
|
|
11615
11746
|
// src/services/knowledgeExpositionService.ts
|
|
11616
11747
|
var EXPOSITION_REL = (lessonCode) => "_sot/expositions/" + lessonCode + ".md";
|
|
11617
11748
|
var ExpositionApprovalError = class extends Error {
|
|
@@ -11729,7 +11860,12 @@ async function ensureKnowledgeExposition(options) {
|
|
|
11729
11860
|
}
|
|
11730
11861
|
const targetLanguage = options.targetLanguage || plan.translation?.target_language || "vi";
|
|
11731
11862
|
const originalSystemPrompt = buildSystemPrompt(targetLanguage, techStack, hardwarePlatform);
|
|
11732
|
-
const
|
|
11863
|
+
const productSpecBlock = buildProductSpecBlock({
|
|
11864
|
+
refPack,
|
|
11865
|
+
briefing: techStack || hardwarePlatform ? { techStack, hardwarePlatform } : void 0,
|
|
11866
|
+
overrides: options.productSpec
|
|
11867
|
+
});
|
|
11868
|
+
const originalUserPrompt = buildUserPrompt(session, plan, glossary, targetLanguage, options.slcMarkdown, refPack) + (productSpecBlock ? "\n\n" + productSpecBlock : "");
|
|
11733
11869
|
const content = (await llmFn(
|
|
11734
11870
|
originalSystemPrompt,
|
|
11735
11871
|
originalUserPrompt
|
|
@@ -11763,7 +11899,17 @@ async function ensureKnowledgeExposition(options) {
|
|
|
11763
11899
|
const parsed = JSON.parse(match[0]);
|
|
11764
11900
|
return { verdict: parsed.verdict ?? "NEEDS_REVISION", score: parsed.score ?? 0, critique: parsed.critique ?? "" };
|
|
11765
11901
|
};
|
|
11766
|
-
|
|
11902
|
+
const scopedKeywords = [
|
|
11903
|
+
...session.new_keywords,
|
|
11904
|
+
...glossary.map((g) => g.term)
|
|
11905
|
+
];
|
|
11906
|
+
const runDeterministic = (candidate) => validateArtifactDeterministic({
|
|
11907
|
+
content: candidate,
|
|
11908
|
+
refPack,
|
|
11909
|
+
scopedKeywords
|
|
11910
|
+
});
|
|
11911
|
+
let det = runDeterministic(finalContent);
|
|
11912
|
+
let verdict = det.ok ? await judgeOnce(finalContent) : { verdict: "NEEDS_REVISION", score: 0, critique: validatorRepairPrompt(det.issues) };
|
|
11767
11913
|
if (verdict.verdict !== "APPROVED") {
|
|
11768
11914
|
const repairPrompt = [
|
|
11769
11915
|
originalUserPrompt,
|
|
@@ -11771,11 +11917,12 @@ async function ensureKnowledgeExposition(options) {
|
|
|
11771
11917
|
"--- YOUR PREVIOUS DRAFT (REJECTED, fix ALL issues below) ---",
|
|
11772
11918
|
finalContent,
|
|
11773
11919
|
"",
|
|
11774
|
-
"---
|
|
11920
|
+
"--- CRITIQUE (fix ALL issues, output the COMPLETE corrected document) ---",
|
|
11775
11921
|
verdict.critique
|
|
11776
11922
|
].join("\n");
|
|
11777
11923
|
finalContent = (await llmFn(originalSystemPrompt, repairPrompt)).trim();
|
|
11778
|
-
|
|
11924
|
+
det = runDeterministic(finalContent);
|
|
11925
|
+
verdict = det.ok ? await judgeOnce(finalContent) : { verdict: "NEEDS_REVISION", score: 0, critique: validatorRepairPrompt(det.issues) };
|
|
11779
11926
|
}
|
|
11780
11927
|
if (verdict.verdict !== "APPROVED") {
|
|
11781
11928
|
throw new Error("EXPOSITION failed judge for " + lessonCode + " (verdict " + verdict.verdict + ", score " + verdict.score + "): " + verdict.critique);
|
|
@@ -26204,6 +26351,10 @@ ${expositionContext}`);
|
|
|
26204
26351
|
parts.push(`### REFERENCE PACK GROUND TRUTH
|
|
26205
26352
|
${effectiveRefPack}`);
|
|
26206
26353
|
}
|
|
26354
|
+
const productSpec = buildProductSpecBlock({ refPack, briefing: options.briefing });
|
|
26355
|
+
if (productSpec) {
|
|
26356
|
+
parts.push(productSpec);
|
|
26357
|
+
}
|
|
26207
26358
|
return parts.length > 0 ? `
|
|
26208
26359
|
|
|
26209
26360
|
[GROUND TRUTH]:
|
|
@@ -26362,6 +26513,23 @@ ${yamlBlock.trim()}
|
|
|
26362
26513
|
`);
|
|
26363
26514
|
};
|
|
26364
26515
|
var injectYamlReviewMetadata = injectYamlReviewMetadata2;
|
|
26516
|
+
const lessonDet = validateArtifactDeterministic({ content: lessonContent, refPack });
|
|
26517
|
+
if (!lessonDet.ok) {
|
|
26518
|
+
const detCritique = validatorRepairPrompt(lessonDet.issues);
|
|
26519
|
+
onProgress?.("@reviewer", `\u26D4 LESSON deterministic validator FAIL: ${detCritique.slice(0, 200)}`);
|
|
26520
|
+
await storage.updateArtifactState(projectId, lessonCode, "LESSON", {
|
|
26521
|
+
state: "rejected",
|
|
26522
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26523
|
+
contentHash: computeContentHash(lessonContent),
|
|
26524
|
+
review: {
|
|
26525
|
+
decision: "NEEDS_REVISION",
|
|
26526
|
+
reviewedBy: "@heuristic-linter",
|
|
26527
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26528
|
+
score: 0,
|
|
26529
|
+
critique: detCritique
|
|
26530
|
+
}
|
|
26531
|
+
});
|
|
26532
|
+
}
|
|
26365
26533
|
const judgeObjectives = parseLessonObjectives(lessonContent, concept, bloomLevel);
|
|
26366
26534
|
const judgeExpositionExcerpt = expositionContext ? buildExpositionExcerpt(expositionContext, { budget: 3e3, sectionLanguageContract: slcMarkdown, artifactType: "KNOWLEDGE_EXPOSITION" }).excerpt : void 0;
|
|
26367
26535
|
const judgeResult = await LLMJudgeEngine.evaluateArtifactWithLLM({
|
|
@@ -26617,6 +26785,23 @@ ${currentContent}` }],
|
|
|
26617
26785
|
${lessonExcerpt}${symbolLedgerBlock}`;
|
|
26618
26786
|
const judgeSat = (sat, content) => {
|
|
26619
26787
|
if (gateModeFor(gates, sat) !== "LLM_JUDGE") return Promise.resolve();
|
|
26788
|
+
const det = validateArtifactDeterministic({ content, refPack });
|
|
26789
|
+
if (!det.ok) {
|
|
26790
|
+
const critique = validatorRepairPrompt(det.issues);
|
|
26791
|
+
onProgress?.("@reviewer", `\u26D4 ${sat} deterministic validator FAIL: ${critique.slice(0, 200)}`);
|
|
26792
|
+
return storage.updateArtifactState(projectId, lessonCode, sat, {
|
|
26793
|
+
state: "rejected",
|
|
26794
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26795
|
+
contentHash: computeContentHash(content),
|
|
26796
|
+
review: {
|
|
26797
|
+
decision: "NEEDS_REVISION",
|
|
26798
|
+
reviewedBy: "@heuristic-linter",
|
|
26799
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26800
|
+
score: 0,
|
|
26801
|
+
critique
|
|
26802
|
+
}
|
|
26803
|
+
});
|
|
26804
|
+
}
|
|
26620
26805
|
return judgeSatelliteArtifact({
|
|
26621
26806
|
storage,
|
|
26622
26807
|
projectId,
|