@thanh01.pmt/curriculum-kit 1.4.21 → 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 +265 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.mjs +265 -11
- package/dist/index.mjs.map +1 -1
- package/dist/workflow/index.cjs +15 -0
- package/dist/workflow/index.cjs.map +1 -1
- package/dist/workflow/index.mjs +15 -0
- package/dist/workflow/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -11519,6 +11519,21 @@ function normalizeHeading(str) {
|
|
|
11519
11519
|
return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
|
|
11520
11520
|
}
|
|
11521
11521
|
function findCanonicalFuzzy(norm) {
|
|
11522
|
+
if (norm.includes("toolchain") || norm.includes("moi truong phat trien") || norm.includes("cong cu") || norm.includes("version") || norm.includes("phan mem") || norm.includes("development environment") || norm.includes("technical overview") || norm.includes("kien truc")) {
|
|
11523
|
+
return "Technical Overview & Architecture Blueprint";
|
|
11524
|
+
}
|
|
11525
|
+
if (norm.includes("pinout") || norm.includes("phan cung") || norm.includes("hardware") || norm.includes("wiring") || norm.includes("ket noi")) {
|
|
11526
|
+
return "Hardware Pinout & Wiring Configuration Matrix";
|
|
11527
|
+
}
|
|
11528
|
+
if (norm.includes("pedagog") || norm.includes("phuong phap") || norm.includes("day hoc") || norm.includes("su pham")) {
|
|
11529
|
+
return "Core Pedagogical Concept Anchor & Real-World Domain Bridge";
|
|
11530
|
+
}
|
|
11531
|
+
if (norm.includes("standard") || norm.includes("tieu chuan") || norm.includes("csta") || norm.includes("cs2023") || norm.includes("chuan academic")) {
|
|
11532
|
+
return "Standards Alignment";
|
|
11533
|
+
}
|
|
11534
|
+
if (norm.includes("roadmap") || norm.includes("lo trinh") || norm.includes("milestone") || norm.includes("giai doan")) {
|
|
11535
|
+
return void 0;
|
|
11536
|
+
}
|
|
11522
11537
|
if (norm.includes("symbol") || norm.includes("identifier ledger") || norm.includes("dinh danh")) {
|
|
11523
11538
|
return "Symbol & Identifier Ledger";
|
|
11524
11539
|
}
|
|
@@ -11609,6 +11624,137 @@ function extractSymbolLedger(lessonMarkdown) {
|
|
|
11609
11624
|
return result;
|
|
11610
11625
|
}
|
|
11611
11626
|
|
|
11627
|
+
// src/services/artifactValidators.ts
|
|
11628
|
+
var CJK_RE = /[\u3400-\u4DBF\u4E00-\u9FFF\u3040-\u30FF\uAC00-\uD7AF]/g;
|
|
11629
|
+
function scanCjkLeaks(content) {
|
|
11630
|
+
const matches = content.match(CJK_RE);
|
|
11631
|
+
if (!matches || matches.length === 0) return null;
|
|
11632
|
+
const lines = [];
|
|
11633
|
+
for (const line of content.split("\n")) {
|
|
11634
|
+
if (CJK_RE.test(line)) lines.push(line.trim().slice(0, 160));
|
|
11635
|
+
}
|
|
11636
|
+
return {
|
|
11637
|
+
code: "cjk-leak",
|
|
11638
|
+
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.`,
|
|
11639
|
+
evidence: lines.slice(0, 5)
|
|
11640
|
+
};
|
|
11641
|
+
}
|
|
11642
|
+
var VERSION_CLAIM_RE = /\b([A-Z][A-Za-z+#.\-]{1,24})\s+(\d{1,2}(?:\.\d{1,2})?)\b/g;
|
|
11643
|
+
function checkVersionGroundTruth(content, refPack) {
|
|
11644
|
+
if (!refPack || !refPack.trim()) return null;
|
|
11645
|
+
const packLower = refPack.toLowerCase();
|
|
11646
|
+
const claims = /* @__PURE__ */ new Map();
|
|
11647
|
+
for (const m of content.matchAll(VERSION_CLAIM_RE)) {
|
|
11648
|
+
const tool = m[1];
|
|
11649
|
+
if (claims.size === 0 || !claims.has(tool)) claims.set(tool, m[2]);
|
|
11650
|
+
}
|
|
11651
|
+
const contradictions = [];
|
|
11652
|
+
for (const [tool, claimed] of claims) {
|
|
11653
|
+
const toolRe = new RegExp(`\\b${tool.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s+(\\d{1,2}(?:\\.\\d{1,2})?)\\b`, "i");
|
|
11654
|
+
const packMatch = packLower.match(toolRe);
|
|
11655
|
+
if (packMatch && packMatch[1] !== claimed) {
|
|
11656
|
+
contradictions.push(`${tool}: artifact n\xF3i ${claimed}, REFERENCE_PACK ch\u1ED1t ${packMatch[1]}`);
|
|
11657
|
+
}
|
|
11658
|
+
}
|
|
11659
|
+
if (contradictions.length === 0) return null;
|
|
11660
|
+
return {
|
|
11661
|
+
code: "version-contradiction",
|
|
11662
|
+
detail: `Version m\xE2u thu\u1EABn v\u1EDBi ground truth REFERENCE_PACK: ${contradictions.join("; ")}. S\u1EEDa theo b\u1EA3n trong REFERENCE_PACK.`,
|
|
11663
|
+
evidence: contradictions
|
|
11664
|
+
};
|
|
11665
|
+
}
|
|
11666
|
+
function checkScopeDrift(content, scopedKeywords, tolerance = 0) {
|
|
11667
|
+
const conceptHeaders = /* @__PURE__ */ new Set();
|
|
11668
|
+
for (const m of content.matchAll(/\*\*([^*\n]{3,60}?)\s*\((?:WHAT|CIO|SIO|ULO)[^)]*\):\*\*/g)) {
|
|
11669
|
+
conceptHeaders.add(m[1].toLowerCase());
|
|
11670
|
+
}
|
|
11671
|
+
if (conceptHeaders.size === 0) return null;
|
|
11672
|
+
const drift = [];
|
|
11673
|
+
for (const header of conceptHeaders) {
|
|
11674
|
+
const hit = scopedKeywords.some((kw) => {
|
|
11675
|
+
const k = kw.toLowerCase().trim();
|
|
11676
|
+
if (!k) return false;
|
|
11677
|
+
const tokens = k.split(/\s+/);
|
|
11678
|
+
const present = tokens.filter((t) => header.includes(t)).length;
|
|
11679
|
+
return present >= Math.max(1, tokens.length - tolerance);
|
|
11680
|
+
});
|
|
11681
|
+
if (!hit) drift.push(header);
|
|
11682
|
+
}
|
|
11683
|
+
if (drift.length === 0) return null;
|
|
11684
|
+
return {
|
|
11685
|
+
code: "scope-drift",
|
|
11686
|
+
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.`,
|
|
11687
|
+
evidence: drift
|
|
11688
|
+
};
|
|
11689
|
+
}
|
|
11690
|
+
function validateArtifactDeterministic(input) {
|
|
11691
|
+
const issues = [];
|
|
11692
|
+
const cjk = scanCjkLeaks(input.content);
|
|
11693
|
+
if (cjk) issues.push(cjk);
|
|
11694
|
+
if (input.refPack) {
|
|
11695
|
+
const ver = checkVersionGroundTruth(input.content, input.refPack);
|
|
11696
|
+
if (ver) issues.push(ver);
|
|
11697
|
+
}
|
|
11698
|
+
if (input.scopedKeywords && input.scopedKeywords.length > 0) {
|
|
11699
|
+
const scope = checkScopeDrift(input.content, input.scopedKeywords);
|
|
11700
|
+
if (scope) issues.push(scope);
|
|
11701
|
+
}
|
|
11702
|
+
return { ok: issues.length === 0, issues };
|
|
11703
|
+
}
|
|
11704
|
+
function validatorRepairPrompt(issues) {
|
|
11705
|
+
return [
|
|
11706
|
+
"DETERMINISTIC VALIDATOR FAILURES (mechanically detected \u2014 non-negotiable, fix ALL):",
|
|
11707
|
+
...issues.map((i, n) => `${n + 1}. [${i.code}] ${i.detail}${i.evidence.length ? `
|
|
11708
|
+
Evidence: ${i.evidence.join(" | ").slice(0, 400)}` : ""}`)
|
|
11709
|
+
].join("\n");
|
|
11710
|
+
}
|
|
11711
|
+
|
|
11712
|
+
// src/services/productSpec.ts
|
|
11713
|
+
function extractToolchainFacts(refPack) {
|
|
11714
|
+
const { excerpt } = buildSectionAwareExcerpt(refPack, {
|
|
11715
|
+
priorities: ["Technical Overview & Architecture Blueprint", "Hardware Pinout & Wiring Configuration Matrix"],
|
|
11716
|
+
budget: 4e3
|
|
11717
|
+
});
|
|
11718
|
+
if (!excerpt) return [];
|
|
11719
|
+
const factLines = [];
|
|
11720
|
+
for (const rawLine of excerpt.split("\n")) {
|
|
11721
|
+
const line = rawLine.trim();
|
|
11722
|
+
if (!line || line.startsWith("#")) continue;
|
|
11723
|
+
if (/\d{1,2}(?:\.\d{1,2})?/.test(line) || /Xcode|Swift|macOS|iOS|SDK|Node|Python|JDK|\.NET/i.test(line)) {
|
|
11724
|
+
factLines.push(line.replace(/^[-*•]\s*/, "- "));
|
|
11725
|
+
}
|
|
11726
|
+
if (factLines.length >= 8) break;
|
|
11727
|
+
}
|
|
11728
|
+
return factLines;
|
|
11729
|
+
}
|
|
11730
|
+
function buildProductSpecBlock(input) {
|
|
11731
|
+
const b = input.briefing || {};
|
|
11732
|
+
const platform = (input.overrides?.primaryLanguage || b.coreTechnology || b.techStack || b.topic || "").trim();
|
|
11733
|
+
const hardware = (b.hardwarePlatform || b.studentEquipment || b.classDynamic || "").trim();
|
|
11734
|
+
const toolchainFacts = input.refPack ? extractToolchainFacts(input.refPack) : [];
|
|
11735
|
+
const lines = [];
|
|
11736
|
+
lines.push("[PRODUCT SPEC \u2014 CANONICAL, BINDING FOR EVERY ARTIFACT OF THIS PROJECT]:");
|
|
11737
|
+
lines.push("All decisions below are PROJECT-WIDE. Every artifact (LESSON, satellites, KX)");
|
|
11738
|
+
lines.push("must describe the SAME product. Contradicting or re-deciding any line here is a");
|
|
11739
|
+
lines.push("consistency failure \u2014 if a needed decision is absent, stay GENERIC, never invent.");
|
|
11740
|
+
lines.push("");
|
|
11741
|
+
if (platform) {
|
|
11742
|
+
lines.push(`- Primary technology: ${platform}`);
|
|
11743
|
+
}
|
|
11744
|
+
if (hardware) {
|
|
11745
|
+
lines.push(`- Hardware / classroom setup: ${hardware}`);
|
|
11746
|
+
}
|
|
11747
|
+
if (input.overrides?.deliverableTemplate) {
|
|
11748
|
+
lines.push(`- Deliverable template: ${input.overrides.deliverableTemplate}`);
|
|
11749
|
+
}
|
|
11750
|
+
if (toolchainFacts.length > 0) {
|
|
11751
|
+
lines.push("- Toolchain & versions (ground truth):");
|
|
11752
|
+
for (const f of toolchainFacts) lines.push(` ${f}`);
|
|
11753
|
+
}
|
|
11754
|
+
if (lines.length <= 5) return "";
|
|
11755
|
+
return lines.join("\n");
|
|
11756
|
+
}
|
|
11757
|
+
|
|
11612
11758
|
// src/services/knowledgeExpositionService.ts
|
|
11613
11759
|
var EXPOSITION_REL = (lessonCode) => "_sot/expositions/" + lessonCode + ".md";
|
|
11614
11760
|
var ExpositionApprovalError = class extends Error {
|
|
@@ -11655,7 +11801,7 @@ function buildSystemPrompt(targetLanguage = "vi", techStack, hardwarePlatform) {
|
|
|
11655
11801
|
langDirective
|
|
11656
11802
|
].join("\n");
|
|
11657
11803
|
}
|
|
11658
|
-
function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMarkdown) {
|
|
11804
|
+
function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMarkdown, refPack) {
|
|
11659
11805
|
const depthLines = session.depth_assignments.map((d) => "- " + d.node_id + " [" + d.depth.toUpperCase() + "]: " + DEPTH_RULES[d.depth]).join("\n");
|
|
11660
11806
|
const termLines = glossary.map((g) => "- " + g.term + (g.definition ? " \u2014 " + g.definition : "") + (g.example ? " (example: " + g.example + ")" : "")).join("\n");
|
|
11661
11807
|
const langCode = resolveTargetLanguageCode(targetLanguage);
|
|
@@ -11668,6 +11814,28 @@ function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMark
|
|
|
11668
11814
|
const hQuestions = headings["Self-Check Questions"] || (langCode === "vi" ? "C\xE2u h\u1ECFi T\u1EF1 ki\u1EC3m tra" : "Self-Check Questions");
|
|
11669
11815
|
const skeleton = `## ${hScope} / ## ${hTerms} / ## ${hNarratives} / ## ${hExamples} / ## ${hMistakes} / ## ${hQuestions}`;
|
|
11670
11816
|
const headingDirective = buildHeadingDirective("KNOWLEDGE_EXPOSITION", slcMarkdown, targetLanguage);
|
|
11817
|
+
let refPackBlock = "";
|
|
11818
|
+
if (refPack && refPack.trim()) {
|
|
11819
|
+
const refExcerpt = buildSectionAwareExcerpt(refPack, {
|
|
11820
|
+
priorities: [
|
|
11821
|
+
"Technical Overview & Architecture Blueprint",
|
|
11822
|
+
"Hardware Pinout & Wiring Configuration Matrix",
|
|
11823
|
+
"Standards Alignment"
|
|
11824
|
+
],
|
|
11825
|
+
budget: 2500
|
|
11826
|
+
}).excerpt;
|
|
11827
|
+
if (refExcerpt) {
|
|
11828
|
+
refPackBlock = [
|
|
11829
|
+
"",
|
|
11830
|
+
"[GROUND TRUTH \u2014 BINDING, from REFERENCE_PACK.md]:",
|
|
11831
|
+
"Tool versions, APIs, and platform facts below are CANONICAL. Do NOT",
|
|
11832
|
+
"contradict them; do NOT substitute versions from memory. If a fact you",
|
|
11833
|
+
"need is not stated here, stay generic rather than inventing specifics.",
|
|
11834
|
+
"",
|
|
11835
|
+
refExcerpt
|
|
11836
|
+
].join("\n");
|
|
11837
|
+
}
|
|
11838
|
+
}
|
|
11671
11839
|
return [
|
|
11672
11840
|
"Session: " + session.id + " \u2014 " + session.title,
|
|
11673
11841
|
"Objective: " + session.prose_objective,
|
|
@@ -11680,6 +11848,7 @@ function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMark
|
|
|
11680
11848
|
"",
|
|
11681
11849
|
"Glossary terms (definitions are canonical):",
|
|
11682
11850
|
termLines || "(none provided \u2014 write definitions and mark them for glossary sync)",
|
|
11851
|
+
refPackBlock,
|
|
11683
11852
|
"",
|
|
11684
11853
|
"Section skeleton to fill (replace ONLY the {{placeholders}}):",
|
|
11685
11854
|
skeleton,
|
|
@@ -11688,7 +11857,7 @@ function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMark
|
|
|
11688
11857
|
].filter(Boolean).join("\n");
|
|
11689
11858
|
}
|
|
11690
11859
|
async function ensureKnowledgeExposition(options) {
|
|
11691
|
-
const { projectId, lessonCode, plan, glossary, llmFn, storage, techStack, hardwarePlatform } = options;
|
|
11860
|
+
const { projectId, lessonCode, plan, glossary, llmFn, storage, techStack, hardwarePlatform, refPack } = options;
|
|
11692
11861
|
const session = plan.sessions.find((s) => s.id === lessonCode);
|
|
11693
11862
|
if (!session) throw new Error("Session " + lessonCode + " not found in plan " + plan.plan_id);
|
|
11694
11863
|
const existing = await storage.readArtifact(projectId, EXPOSITION_REL(lessonCode));
|
|
@@ -11702,9 +11871,16 @@ async function ensureKnowledgeExposition(options) {
|
|
|
11702
11871
|
throw new ExpositionApprovalError("Approval hash " + plan.approval.plan_hash + " does not match current plan hash " + plan.plan_hash + " \u2014 re-approve after plan changes");
|
|
11703
11872
|
}
|
|
11704
11873
|
const targetLanguage = options.targetLanguage || plan.translation?.target_language || "vi";
|
|
11874
|
+
const originalSystemPrompt = buildSystemPrompt(targetLanguage, techStack, hardwarePlatform);
|
|
11875
|
+
const productSpecBlock = buildProductSpecBlock({
|
|
11876
|
+
refPack,
|
|
11877
|
+
briefing: techStack || hardwarePlatform ? { techStack, hardwarePlatform } : void 0,
|
|
11878
|
+
overrides: options.productSpec
|
|
11879
|
+
});
|
|
11880
|
+
const originalUserPrompt = buildUserPrompt(session, plan, glossary, targetLanguage, options.slcMarkdown, refPack) + (productSpecBlock ? "\n\n" + productSpecBlock : "");
|
|
11705
11881
|
const content = (await llmFn(
|
|
11706
|
-
|
|
11707
|
-
|
|
11882
|
+
originalSystemPrompt,
|
|
11883
|
+
originalUserPrompt
|
|
11708
11884
|
)).trim();
|
|
11709
11885
|
if (content.length < 200) {
|
|
11710
11886
|
throw new Error("EXPOSITION too short for " + lessonCode + " (" + content.length + " chars) \u2014 refusing to save");
|
|
@@ -11735,11 +11911,30 @@ async function ensureKnowledgeExposition(options) {
|
|
|
11735
11911
|
const parsed = JSON.parse(match[0]);
|
|
11736
11912
|
return { verdict: parsed.verdict ?? "NEEDS_REVISION", score: parsed.score ?? 0, critique: parsed.critique ?? "" };
|
|
11737
11913
|
};
|
|
11738
|
-
|
|
11914
|
+
const scopedKeywords = [
|
|
11915
|
+
...session.new_keywords,
|
|
11916
|
+
...glossary.map((g) => g.term)
|
|
11917
|
+
];
|
|
11918
|
+
const runDeterministic = (candidate) => validateArtifactDeterministic({
|
|
11919
|
+
content: candidate,
|
|
11920
|
+
refPack,
|
|
11921
|
+
scopedKeywords
|
|
11922
|
+
});
|
|
11923
|
+
let det = runDeterministic(finalContent);
|
|
11924
|
+
let verdict = det.ok ? await judgeOnce(finalContent) : { verdict: "NEEDS_REVISION", score: 0, critique: validatorRepairPrompt(det.issues) };
|
|
11739
11925
|
if (verdict.verdict !== "APPROVED") {
|
|
11740
|
-
const repairPrompt =
|
|
11741
|
-
|
|
11742
|
-
|
|
11926
|
+
const repairPrompt = [
|
|
11927
|
+
originalUserPrompt,
|
|
11928
|
+
"",
|
|
11929
|
+
"--- YOUR PREVIOUS DRAFT (REJECTED, fix ALL issues below) ---",
|
|
11930
|
+
finalContent,
|
|
11931
|
+
"",
|
|
11932
|
+
"--- CRITIQUE (fix ALL issues, output the COMPLETE corrected document) ---",
|
|
11933
|
+
verdict.critique
|
|
11934
|
+
].join("\n");
|
|
11935
|
+
finalContent = (await llmFn(originalSystemPrompt, repairPrompt)).trim();
|
|
11936
|
+
det = runDeterministic(finalContent);
|
|
11937
|
+
verdict = det.ok ? await judgeOnce(finalContent) : { verdict: "NEEDS_REVISION", score: 0, critique: validatorRepairPrompt(det.issues) };
|
|
11743
11938
|
}
|
|
11744
11939
|
if (verdict.verdict !== "APPROVED") {
|
|
11745
11940
|
throw new Error("EXPOSITION failed judge for " + lessonCode + " (verdict " + verdict.verdict + ", score " + verdict.score + "): " + verdict.critique);
|
|
@@ -11779,12 +11974,27 @@ async function ensureExpositionForLesson(storage, projectId, lessonCode, options
|
|
|
11779
11974
|
try {
|
|
11780
11975
|
const lpRaw = await storage.readSotDocument(projectId, "LEARNER_PROFILE.md");
|
|
11781
11976
|
if (lpRaw) {
|
|
11782
|
-
const
|
|
11783
|
-
|
|
11977
|
+
const hwPatterns = [
|
|
11978
|
+
/(?:Student Equipment|Hardware|Platform|Thiết bị|Thiết bị học tập)\s*(?:\(|:|\*)*\s*([^\n\r]+?)(?:\s*\*\*|[.).]?\s*$)/i,
|
|
11979
|
+
/(?:máy|device|machine)\s+([^,.;\n]{4,60}(?:M\d|Intel|PC|computer|mini)[^,.;\n]{0,30})/i
|
|
11980
|
+
];
|
|
11981
|
+
for (const p of hwPatterns) {
|
|
11982
|
+
const m = lpRaw.match(p);
|
|
11983
|
+
if (m && !hw) {
|
|
11984
|
+
hw = m[1].trim();
|
|
11985
|
+
break;
|
|
11986
|
+
}
|
|
11987
|
+
}
|
|
11784
11988
|
}
|
|
11785
11989
|
} catch {
|
|
11786
11990
|
}
|
|
11787
11991
|
}
|
|
11992
|
+
let refPackContent;
|
|
11993
|
+
try {
|
|
11994
|
+
refPackContent = await storage.readSotDocument(projectId, "REFERENCE_PACK.md") || void 0;
|
|
11995
|
+
} catch {
|
|
11996
|
+
refPackContent = void 0;
|
|
11997
|
+
}
|
|
11788
11998
|
const { runCurriculumAIInference: runCurriculumAIInference2 } = await Promise.resolve().then(() => (init_streamRunner(), streamRunner_exports));
|
|
11789
11999
|
const targetLang = options.targetLanguage || plan.translation?.target_language || "vi";
|
|
11790
12000
|
const result = await ensureKnowledgeExposition({
|
|
@@ -11796,6 +12006,7 @@ async function ensureExpositionForLesson(storage, projectId, lessonCode, options
|
|
|
11796
12006
|
slcMarkdown: options.slcMarkdown,
|
|
11797
12007
|
techStack: tech,
|
|
11798
12008
|
hardwarePlatform: hw,
|
|
12009
|
+
refPack: refPackContent,
|
|
11799
12010
|
llmFn: async (systemPrompt, userPrompt) => {
|
|
11800
12011
|
const runnerOpts = options.customInference ? { customInference: options.customInference } : {};
|
|
11801
12012
|
const out = await runCurriculumAIInference2(
|
|
@@ -26094,11 +26305,16 @@ async function produceSingleLesson(storage, projectId, lessonId, options = {}) {
|
|
|
26094
26305
|
}).excerpt : "";
|
|
26095
26306
|
let effectiveRefPack = refPack ? buildSectionAwareExcerpt(refPack, {
|
|
26096
26307
|
priorities: [
|
|
26308
|
+
// Toolchain/version matrix FIRST — the most-frequently-hallucinated
|
|
26309
|
+
// facts are tool versions ("Xcode 15" vs ground truth "Xcode 16").
|
|
26310
|
+
// The bilingual fuzzy matcher in contextBuilder resolves these keys
|
|
26311
|
+
// even when the generated RefPack headings are Vietnamese.
|
|
26097
26312
|
"Technical Overview & Architecture Blueprint",
|
|
26098
26313
|
"Hardware Pinout & Wiring Configuration Matrix",
|
|
26314
|
+
"Standards Alignment",
|
|
26099
26315
|
"Core Pedagogical Concept Anchor & Real-World Domain Bridge"
|
|
26100
26316
|
],
|
|
26101
|
-
budget:
|
|
26317
|
+
budget: 3500
|
|
26102
26318
|
}).excerpt : "";
|
|
26103
26319
|
const baseContextPrefix = `PROJECT & ACADEMIC CONTEXT:
|
|
26104
26320
|
- Project ID: ${projectId}
|
|
@@ -26147,6 +26363,10 @@ ${expositionContext}`);
|
|
|
26147
26363
|
parts.push(`### REFERENCE PACK GROUND TRUTH
|
|
26148
26364
|
${effectiveRefPack}`);
|
|
26149
26365
|
}
|
|
26366
|
+
const productSpec = buildProductSpecBlock({ refPack, briefing: options.briefing });
|
|
26367
|
+
if (productSpec) {
|
|
26368
|
+
parts.push(productSpec);
|
|
26369
|
+
}
|
|
26150
26370
|
return parts.length > 0 ? `
|
|
26151
26371
|
|
|
26152
26372
|
[GROUND TRUTH]:
|
|
@@ -26305,6 +26525,23 @@ ${yamlBlock.trim()}
|
|
|
26305
26525
|
`);
|
|
26306
26526
|
};
|
|
26307
26527
|
var injectYamlReviewMetadata = injectYamlReviewMetadata2;
|
|
26528
|
+
const lessonDet = validateArtifactDeterministic({ content: lessonContent, refPack });
|
|
26529
|
+
if (!lessonDet.ok) {
|
|
26530
|
+
const detCritique = validatorRepairPrompt(lessonDet.issues);
|
|
26531
|
+
onProgress?.("@reviewer", `\u26D4 LESSON deterministic validator FAIL: ${detCritique.slice(0, 200)}`);
|
|
26532
|
+
await storage.updateArtifactState(projectId, lessonCode, "LESSON", {
|
|
26533
|
+
state: "rejected",
|
|
26534
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26535
|
+
contentHash: computeContentHash(lessonContent),
|
|
26536
|
+
review: {
|
|
26537
|
+
decision: "NEEDS_REVISION",
|
|
26538
|
+
reviewedBy: "@heuristic-linter",
|
|
26539
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26540
|
+
score: 0,
|
|
26541
|
+
critique: detCritique
|
|
26542
|
+
}
|
|
26543
|
+
});
|
|
26544
|
+
}
|
|
26308
26545
|
const judgeObjectives = parseLessonObjectives(lessonContent, concept, bloomLevel);
|
|
26309
26546
|
const judgeExpositionExcerpt = expositionContext ? buildExpositionExcerpt(expositionContext, { budget: 3e3, sectionLanguageContract: slcMarkdown, artifactType: "KNOWLEDGE_EXPOSITION" }).excerpt : void 0;
|
|
26310
26547
|
const judgeResult = await LLMJudgeEngine.evaluateArtifactWithLLM({
|
|
@@ -26560,6 +26797,23 @@ ${currentContent}` }],
|
|
|
26560
26797
|
${lessonExcerpt}${symbolLedgerBlock}`;
|
|
26561
26798
|
const judgeSat = (sat, content) => {
|
|
26562
26799
|
if (gateModeFor(gates, sat) !== "LLM_JUDGE") return Promise.resolve();
|
|
26800
|
+
const det = validateArtifactDeterministic({ content, refPack });
|
|
26801
|
+
if (!det.ok) {
|
|
26802
|
+
const critique = validatorRepairPrompt(det.issues);
|
|
26803
|
+
onProgress?.("@reviewer", `\u26D4 ${sat} deterministic validator FAIL: ${critique.slice(0, 200)}`);
|
|
26804
|
+
return storage.updateArtifactState(projectId, lessonCode, sat, {
|
|
26805
|
+
state: "rejected",
|
|
26806
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26807
|
+
contentHash: computeContentHash(content),
|
|
26808
|
+
review: {
|
|
26809
|
+
decision: "NEEDS_REVISION",
|
|
26810
|
+
reviewedBy: "@heuristic-linter",
|
|
26811
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26812
|
+
score: 0,
|
|
26813
|
+
critique
|
|
26814
|
+
}
|
|
26815
|
+
});
|
|
26816
|
+
}
|
|
26563
26817
|
return judgeSatelliteArtifact({
|
|
26564
26818
|
storage,
|
|
26565
26819
|
projectId,
|