@thanh01.pmt/curriculum-kit 1.4.22 → 1.4.24
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 +234 -23
- 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 +234 -23
- 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.cjs
CHANGED
|
@@ -11624,6 +11624,137 @@ function extractSymbolLedger(lessonMarkdown) {
|
|
|
11624
11624
|
return result;
|
|
11625
11625
|
}
|
|
11626
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
|
+
|
|
11627
11758
|
// src/services/knowledgeExpositionService.ts
|
|
11628
11759
|
var EXPOSITION_REL = (lessonCode) => "_sot/expositions/" + lessonCode + ".md";
|
|
11629
11760
|
var ExpositionApprovalError = class extends Error {
|
|
@@ -11741,7 +11872,12 @@ async function ensureKnowledgeExposition(options) {
|
|
|
11741
11872
|
}
|
|
11742
11873
|
const targetLanguage = options.targetLanguage || plan.translation?.target_language || "vi";
|
|
11743
11874
|
const originalSystemPrompt = buildSystemPrompt(targetLanguage, techStack, hardwarePlatform);
|
|
11744
|
-
const
|
|
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 : "");
|
|
11745
11881
|
const content = (await llmFn(
|
|
11746
11882
|
originalSystemPrompt,
|
|
11747
11883
|
originalUserPrompt
|
|
@@ -11775,7 +11911,17 @@ async function ensureKnowledgeExposition(options) {
|
|
|
11775
11911
|
const parsed = JSON.parse(match[0]);
|
|
11776
11912
|
return { verdict: parsed.verdict ?? "NEEDS_REVISION", score: parsed.score ?? 0, critique: parsed.critique ?? "" };
|
|
11777
11913
|
};
|
|
11778
|
-
|
|
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) };
|
|
11779
11925
|
if (verdict.verdict !== "APPROVED") {
|
|
11780
11926
|
const repairPrompt = [
|
|
11781
11927
|
originalUserPrompt,
|
|
@@ -11783,11 +11929,12 @@ async function ensureKnowledgeExposition(options) {
|
|
|
11783
11929
|
"--- YOUR PREVIOUS DRAFT (REJECTED, fix ALL issues below) ---",
|
|
11784
11930
|
finalContent,
|
|
11785
11931
|
"",
|
|
11786
|
-
"---
|
|
11932
|
+
"--- CRITIQUE (fix ALL issues, output the COMPLETE corrected document) ---",
|
|
11787
11933
|
verdict.critique
|
|
11788
11934
|
].join("\n");
|
|
11789
11935
|
finalContent = (await llmFn(originalSystemPrompt, repairPrompt)).trim();
|
|
11790
|
-
|
|
11936
|
+
det = runDeterministic(finalContent);
|
|
11937
|
+
verdict = det.ok ? await judgeOnce(finalContent) : { verdict: "NEEDS_REVISION", score: 0, critique: validatorRepairPrompt(det.issues) };
|
|
11791
11938
|
}
|
|
11792
11939
|
if (verdict.verdict !== "APPROVED") {
|
|
11793
11940
|
throw new Error("EXPOSITION failed judge for " + lessonCode + " (verdict " + verdict.verdict + ", score " + verdict.score + "): " + verdict.critique);
|
|
@@ -26056,7 +26203,22 @@ async function produceSingleLesson(storage, projectId, lessonId, options = {}) {
|
|
|
26056
26203
|
const framework = await storage.readSotDocument(projectId, "CURRICULUM_FRAMEWORK.md") || "";
|
|
26057
26204
|
const styleGuide = await storage.readSotDocument(projectId, "CONTENT_STYLE_GUIDE.md") || "";
|
|
26058
26205
|
const refPack = await storage.readSotDocument(projectId, "REFERENCE_PACK.md") || "";
|
|
26206
|
+
let currentPlanHash;
|
|
26207
|
+
try {
|
|
26208
|
+
const planRaw0 = await storage.readArtifact(projectId, "_sot/CURRICULUM_PLAN.json");
|
|
26209
|
+
if (planRaw0) currentPlanHash = JSON.parse(planRaw0).plan_hash;
|
|
26210
|
+
} catch {
|
|
26211
|
+
}
|
|
26059
26212
|
await storage.readSotDocument(projectId, "LEARNER_PROFILE.md") || "";
|
|
26213
|
+
const isStaleVsPlan = (content) => {
|
|
26214
|
+
if (!content || !currentPlanHash) return false;
|
|
26215
|
+
const m = content.match(/plan_hash:\s*"([0-9a-f]+)"/);
|
|
26216
|
+
return !!m && m[1] !== currentPlanHash;
|
|
26217
|
+
};
|
|
26218
|
+
const stampPlanHash = (content) => {
|
|
26219
|
+
if (!currentPlanHash || content.includes('plan_hash: "' + currentPlanHash + '"')) return content;
|
|
26220
|
+
return content + '\n---\nplan_hash: "' + currentPlanHash + '"\n- **Plan reference:** ' + currentPlanHash + "\n";
|
|
26221
|
+
};
|
|
26060
26222
|
const slcMarkdown = options.sectionLanguageContract || await storage.readSotDocument(projectId, "SECTION_LANGUAGE_CONTRACT.md") || "";
|
|
26061
26223
|
let targetLang = options.targetLanguage;
|
|
26062
26224
|
if (!targetLang) {
|
|
@@ -26216,6 +26378,10 @@ ${expositionContext}`);
|
|
|
26216
26378
|
parts.push(`### REFERENCE PACK GROUND TRUTH
|
|
26217
26379
|
${effectiveRefPack}`);
|
|
26218
26380
|
}
|
|
26381
|
+
const productSpec = buildProductSpecBlock({ refPack, briefing: options.briefing });
|
|
26382
|
+
if (productSpec) {
|
|
26383
|
+
parts.push(productSpec);
|
|
26384
|
+
}
|
|
26219
26385
|
return parts.length > 0 ? `
|
|
26220
26386
|
|
|
26221
26387
|
[GROUND TRUTH]:
|
|
@@ -26263,7 +26429,10 @@ ${sg}${standardsBlock}${glossaryBlock}${buildGroundTruthBlock()}${sessionSliceBl
|
|
|
26263
26429
|
const lessonRelPath = `_content/${unitCode}/LESSON_${lessonCode}.md`;
|
|
26264
26430
|
const existingLessonContent = await storage.readArtifact(projectId, lessonRelPath);
|
|
26265
26431
|
let lessonContent = existingLessonContent || "";
|
|
26266
|
-
if (artifactScope.includes("LESSON") && (!existingLessonContent || force)) {
|
|
26432
|
+
if (artifactScope.includes("LESSON") && (!existingLessonContent || force || isStaleVsPlan(existingLessonContent))) {
|
|
26433
|
+
if (existingLessonContent && !force && isStaleVsPlan(existingLessonContent)) {
|
|
26434
|
+
onProgress?.("@content", `[STALENESS] LESSON_${lessonCode} was generated under a different plan_hash \u2014 regenerating against the current plan.`);
|
|
26435
|
+
}
|
|
26267
26436
|
onProgress?.("@content", `[1/4] Authoring Master Lesson Plan (${pedagogyLabel}): \`LESSON_${lessonCode}.md\` (${lessonTitle})...`);
|
|
26268
26437
|
const dateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
26269
26438
|
const mediaDirective = options.mediaPolicy && options.mediaPolicy.maxGenImagesPerArtifact > 0 ? `
|
|
@@ -26334,7 +26503,7 @@ Fix ALL issues above and output the complete corrected document.` }],
|
|
|
26334
26503
|
} else {
|
|
26335
26504
|
lessonContent = lintReport.autoFixedContent || rawLesson;
|
|
26336
26505
|
}
|
|
26337
|
-
await storage.saveArtifact(projectId, lessonRelPath, lessonContent);
|
|
26506
|
+
await storage.saveArtifact(projectId, lessonRelPath, stampPlanHash(lessonContent));
|
|
26338
26507
|
producedArtifacts.push(`LESSON_${lessonCode}.md`);
|
|
26339
26508
|
}
|
|
26340
26509
|
const contentHash = lessonContent ? computeContentHash(lessonContent) : void 0;
|
|
@@ -26374,6 +26543,23 @@ ${yamlBlock.trim()}
|
|
|
26374
26543
|
`);
|
|
26375
26544
|
};
|
|
26376
26545
|
var injectYamlReviewMetadata = injectYamlReviewMetadata2;
|
|
26546
|
+
const lessonDet = validateArtifactDeterministic({ content: lessonContent, refPack });
|
|
26547
|
+
if (!lessonDet.ok) {
|
|
26548
|
+
const detCritique = validatorRepairPrompt(lessonDet.issues);
|
|
26549
|
+
onProgress?.("@reviewer", `\u26D4 LESSON deterministic validator FAIL: ${detCritique.slice(0, 200)}`);
|
|
26550
|
+
await storage.updateArtifactState(projectId, lessonCode, "LESSON", {
|
|
26551
|
+
state: "rejected",
|
|
26552
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26553
|
+
contentHash: computeContentHash(lessonContent),
|
|
26554
|
+
review: {
|
|
26555
|
+
decision: "NEEDS_REVISION",
|
|
26556
|
+
reviewedBy: "@heuristic-linter",
|
|
26557
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26558
|
+
score: 0,
|
|
26559
|
+
critique: detCritique
|
|
26560
|
+
}
|
|
26561
|
+
});
|
|
26562
|
+
}
|
|
26377
26563
|
const judgeObjectives = parseLessonObjectives(lessonContent, concept, bloomLevel);
|
|
26378
26564
|
const judgeExpositionExcerpt = expositionContext ? buildExpositionExcerpt(expositionContext, { budget: 3e3, sectionLanguageContract: slcMarkdown, artifactType: "KNOWLEDGE_EXPOSITION" }).excerpt : void 0;
|
|
26379
26565
|
const judgeResult = await LLMJudgeEngine.evaluateArtifactWithLLM({
|
|
@@ -26629,6 +26815,23 @@ ${currentContent}` }],
|
|
|
26629
26815
|
${lessonExcerpt}${symbolLedgerBlock}`;
|
|
26630
26816
|
const judgeSat = (sat, content) => {
|
|
26631
26817
|
if (gateModeFor(gates, sat) !== "LLM_JUDGE") return Promise.resolve();
|
|
26818
|
+
const det = validateArtifactDeterministic({ content, refPack });
|
|
26819
|
+
if (!det.ok) {
|
|
26820
|
+
const critique = validatorRepairPrompt(det.issues);
|
|
26821
|
+
onProgress?.("@reviewer", `\u26D4 ${sat} deterministic validator FAIL: ${critique.slice(0, 200)}`);
|
|
26822
|
+
return storage.updateArtifactState(projectId, lessonCode, sat, {
|
|
26823
|
+
state: "rejected",
|
|
26824
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26825
|
+
contentHash: computeContentHash(content),
|
|
26826
|
+
review: {
|
|
26827
|
+
decision: "NEEDS_REVISION",
|
|
26828
|
+
reviewedBy: "@heuristic-linter",
|
|
26829
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26830
|
+
score: 0,
|
|
26831
|
+
critique
|
|
26832
|
+
}
|
|
26833
|
+
});
|
|
26834
|
+
}
|
|
26632
26835
|
return judgeSatelliteArtifact({
|
|
26633
26836
|
storage,
|
|
26634
26837
|
projectId,
|
|
@@ -26645,7 +26848,8 @@ ${lessonExcerpt}${symbolLedgerBlock}`;
|
|
|
26645
26848
|
});
|
|
26646
26849
|
};
|
|
26647
26850
|
const actRelPath = `_content/${unitCode}/ACT_${lessonCode}.md`;
|
|
26648
|
-
|
|
26851
|
+
const existingAct = await storage.readArtifact(projectId, actRelPath);
|
|
26852
|
+
if (artifactScope.includes("ACT") && (!existingAct || force || isStaleVsPlan(existingAct))) {
|
|
26649
26853
|
onProgress?.("@activity", `[2/4] Authoring Hands-on Lab: \`ACT_${lessonCode}.md\`...`);
|
|
26650
26854
|
const actDateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
26651
26855
|
const actPrompt = `You are @activity (Lead Hands-on STEM & Laboratory Exercise Designer).
|
|
@@ -26705,7 +26909,7 @@ ${buildHeadingDirective("ACT", slcMarkdown, targetLang)}`;
|
|
|
26705
26909
|
rawError: rawAct || "Empty AI response"
|
|
26706
26910
|
});
|
|
26707
26911
|
}
|
|
26708
|
-
await storage.saveArtifact(projectId, actRelPath, rawAct);
|
|
26912
|
+
await storage.saveArtifact(projectId, actRelPath, stampPlanHash(rawAct));
|
|
26709
26913
|
await storage.updateArtifactState(projectId, lessonCode, "ACT", {
|
|
26710
26914
|
state: "completed",
|
|
26711
26915
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -26715,7 +26919,8 @@ ${buildHeadingDirective("ACT", slcMarkdown, targetLang)}`;
|
|
|
26715
26919
|
await judgeSat("ACT", rawAct);
|
|
26716
26920
|
}
|
|
26717
26921
|
const quizRelPath = `_content/${unitCode}/QUIZ_${lessonCode}.md`;
|
|
26718
|
-
|
|
26922
|
+
const existingQuiz = await storage.readArtifact(projectId, quizRelPath);
|
|
26923
|
+
if (artifactScope.includes("QUIZ") && (!existingQuiz || force || isStaleVsPlan(existingQuiz))) {
|
|
26719
26924
|
onProgress?.("@assessor", `[3/4] Authoring Assessment Bank: \`QUIZ_${lessonCode}.md\`...`);
|
|
26720
26925
|
const quizDateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
26721
26926
|
const quizPrompt = `You are @assessor (Lead Psychometrician & Competency Assessment Specialist).
|
|
@@ -26769,7 +26974,7 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
|
|
|
26769
26974
|
rawError: rawQuiz || "Empty AI response"
|
|
26770
26975
|
});
|
|
26771
26976
|
}
|
|
26772
|
-
await storage.saveArtifact(projectId, quizRelPath, rawQuiz);
|
|
26977
|
+
await storage.saveArtifact(projectId, quizRelPath, stampPlanHash(rawQuiz));
|
|
26773
26978
|
await storage.updateArtifactState(projectId, lessonCode, "QUIZ", {
|
|
26774
26979
|
state: "completed",
|
|
26775
26980
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -26779,7 +26984,8 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
|
|
|
26779
26984
|
await judgeSat("QUIZ", rawQuiz);
|
|
26780
26985
|
}
|
|
26781
26986
|
const slideRelPath = `_content/${unitCode}/SLIDE_${lessonCode}.md`;
|
|
26782
|
-
|
|
26987
|
+
const existingSlide = await storage.readArtifact(projectId, slideRelPath);
|
|
26988
|
+
if (artifactScope.includes("SLIDE") && (!existingSlide || force || isStaleVsPlan(existingSlide))) {
|
|
26783
26989
|
onProgress?.("@illustrator", `[4/4] Authoring Presentation Slide Deck: \`SLIDE_${lessonCode}.md\`...`);
|
|
26784
26990
|
const isHtmlEngine = options.slidesEngine !== "marp";
|
|
26785
26991
|
if (isHtmlEngine) {
|
|
@@ -26813,7 +27019,7 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
|
|
|
26813
27019
|
let deckJson = workflowResult.deckJson;
|
|
26814
27020
|
const markdownWrapper = workflowResult.markdownWrapper;
|
|
26815
27021
|
let validation = presentationKitAi?.validateHtmlSlideDeck ? presentationKitAi.validateHtmlSlideDeck(deckJson, true) : deckJson && Array.isArray(deckJson.slides) ? { valid: true, score: 95, issues: [], slideCount: deckJson.slides.length} : { valid: false, score: 0, issues: [{ message: "Malformed JSON slide deck" }]};
|
|
26816
|
-
await storage.saveArtifact(projectId, slideRelPath, markdownWrapper);
|
|
27022
|
+
await storage.saveArtifact(projectId, slideRelPath, stampPlanHash(markdownWrapper));
|
|
26817
27023
|
producedArtifacts.push(`SLIDE_${lessonCode}.md`);
|
|
26818
27024
|
if (deckJson) {
|
|
26819
27025
|
const deckJsonStr = JSON.stringify(deckJson, null, 2);
|
|
@@ -26904,7 +27110,7 @@ footer: "Curriculum OS \u2022 ${lessonCode} (${pedagogyLabel})"
|
|
|
26904
27110
|
rawError: rawSlide || "Empty AI response"
|
|
26905
27111
|
});
|
|
26906
27112
|
}
|
|
26907
|
-
await storage.saveArtifact(projectId, slideRelPath, rawSlide);
|
|
27113
|
+
await storage.saveArtifact(projectId, slideRelPath, stampPlanHash(rawSlide));
|
|
26908
27114
|
await storage.updateArtifactState(projectId, lessonCode, "SLIDE", {
|
|
26909
27115
|
state: "completed",
|
|
26910
27116
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -26915,7 +27121,8 @@ footer: "Curriculum OS \u2022 ${lessonCode} (${pedagogyLabel})"
|
|
|
26915
27121
|
}
|
|
26916
27122
|
}
|
|
26917
27123
|
const guideRelPath = `_content/${unitCode}/GUIDE_${lessonCode}.md`;
|
|
26918
|
-
|
|
27124
|
+
const existingGuide = await storage.readArtifact(projectId, guideRelPath);
|
|
27125
|
+
if (artifactScope.includes("GUIDE") && (!existingGuide || force || isStaleVsPlan(existingGuide))) {
|
|
26919
27126
|
onProgress?.("@content", `Authoring Teacher Guide: \`GUIDE_${lessonCode}.md\`...`, { artifactType: "GUIDE" });
|
|
26920
27127
|
const guideDateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
26921
27128
|
const guidePrompt = `You are @content (Senior Curriculum Developer & Teacher Facilitation Specialist).
|
|
@@ -26955,7 +27162,7 @@ ${buildHeadingDirective("GUIDE", slcMarkdown, targetLang)}`;
|
|
|
26955
27162
|
}
|
|
26956
27163
|
);
|
|
26957
27164
|
if (rawGuide && !rawGuide.startsWith("\u26A0\uFE0F") && rawGuide.length >= 200) {
|
|
26958
|
-
await storage.saveArtifact(projectId, guideRelPath, rawGuide);
|
|
27165
|
+
await storage.saveArtifact(projectId, guideRelPath, stampPlanHash(rawGuide));
|
|
26959
27166
|
await storage.updateArtifactState(projectId, lessonCode, "GUIDE", {
|
|
26960
27167
|
state: "completed",
|
|
26961
27168
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -26966,7 +27173,8 @@ ${buildHeadingDirective("GUIDE", slcMarkdown, targetLang)}`;
|
|
|
26966
27173
|
}
|
|
26967
27174
|
}
|
|
26968
27175
|
const handoutRelPath = `_content/${unitCode}/HANDOUT_${lessonCode}.md`;
|
|
26969
|
-
|
|
27176
|
+
const existingHandout = await storage.readArtifact(projectId, handoutRelPath);
|
|
27177
|
+
if (artifactScope.includes("HANDOUT") && (!existingHandout || force || isStaleVsPlan(existingHandout))) {
|
|
26970
27178
|
onProgress?.("@content", `Authoring Student Handout: \`HANDOUT_${lessonCode}.md\`...`, { artifactType: "HANDOUT" });
|
|
26971
27179
|
const handoutDateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
26972
27180
|
const handoutPrompt = `You are @content (Educational Content Specialist).
|
|
@@ -27006,7 +27214,7 @@ ${buildHeadingDirective("HANDOUT", slcMarkdown, targetLang)}`;
|
|
|
27006
27214
|
}
|
|
27007
27215
|
);
|
|
27008
27216
|
if (rawHandout && !rawHandout.startsWith("\u26A0\uFE0F") && rawHandout.length >= 200) {
|
|
27009
|
-
await storage.saveArtifact(projectId, handoutRelPath, rawHandout);
|
|
27217
|
+
await storage.saveArtifact(projectId, handoutRelPath, stampPlanHash(rawHandout));
|
|
27010
27218
|
await storage.updateArtifactState(projectId, lessonCode, "HANDOUT", {
|
|
27011
27219
|
state: "completed",
|
|
27012
27220
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -27017,7 +27225,8 @@ ${buildHeadingDirective("HANDOUT", slcMarkdown, targetLang)}`;
|
|
|
27017
27225
|
}
|
|
27018
27226
|
}
|
|
27019
27227
|
const wksRelPath = `_content/${unitCode}/WKS_${lessonCode}.md`;
|
|
27020
|
-
|
|
27228
|
+
const existingWks = await storage.readArtifact(projectId, wksRelPath);
|
|
27229
|
+
if (artifactScope.includes("WKS") && (!existingWks || force || isStaleVsPlan(existingWks))) {
|
|
27021
27230
|
onProgress?.("@content", `Authoring Student Worksheet: \`WKS_${lessonCode}.md\`...`, { artifactType: "WKS" });
|
|
27022
27231
|
const wksDateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
27023
27232
|
const wksPrompt = `You are @content (Instructional Activity & Worksheet Designer).
|
|
@@ -27061,7 +27270,7 @@ ${buildHeadingDirective("WKS", slcMarkdown, targetLang)}`;
|
|
|
27061
27270
|
}
|
|
27062
27271
|
);
|
|
27063
27272
|
if (rawWks && !rawWks.startsWith("\u26A0\uFE0F") && rawWks.length >= 200) {
|
|
27064
|
-
await storage.saveArtifact(projectId, wksRelPath, rawWks);
|
|
27273
|
+
await storage.saveArtifact(projectId, wksRelPath, stampPlanHash(rawWks));
|
|
27065
27274
|
await storage.updateArtifactState(projectId, lessonCode, "WKS", {
|
|
27066
27275
|
state: "completed",
|
|
27067
27276
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -27072,7 +27281,8 @@ ${buildHeadingDirective("WKS", slcMarkdown, targetLang)}`;
|
|
|
27072
27281
|
}
|
|
27073
27282
|
}
|
|
27074
27283
|
const codeRelPath = `_content/${unitCode}/CODE_${lessonCode}.md`;
|
|
27075
|
-
|
|
27284
|
+
const existingCode = await storage.readArtifact(projectId, codeRelPath);
|
|
27285
|
+
if ((artifactScope.includes("CODE") || artifactScope.includes("CODE_LAB")) && (!existingCode || force || isStaleVsPlan(existingCode))) {
|
|
27076
27286
|
onProgress?.("@activity", `Authoring Executable Code Lab: \`CODE_${lessonCode}.md\`...`, { artifactType: "CODE" });
|
|
27077
27287
|
const codeDateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
27078
27288
|
const codePrompt = `You are @activity & @tech_sme (Lead Software Engineer & Technical SME).
|
|
@@ -27120,7 +27330,7 @@ ${buildHeadingDirective("CODE", slcMarkdown, targetLang)}`;
|
|
|
27120
27330
|
}
|
|
27121
27331
|
);
|
|
27122
27332
|
if (rawCode && !rawCode.startsWith("\u26A0\uFE0F") && rawCode.length >= 200) {
|
|
27123
|
-
await storage.saveArtifact(projectId, codeRelPath, rawCode);
|
|
27333
|
+
await storage.saveArtifact(projectId, codeRelPath, stampPlanHash(rawCode));
|
|
27124
27334
|
await storage.updateArtifactState(projectId, lessonCode, "CODE", {
|
|
27125
27335
|
state: "completed",
|
|
27126
27336
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -27136,7 +27346,8 @@ ${buildHeadingDirective("CODE", slcMarkdown, targetLang)}`;
|
|
|
27136
27346
|
}
|
|
27137
27347
|
}
|
|
27138
27348
|
const extRelPath = `_content/${unitCode}/EXT_${lessonCode}.md`;
|
|
27139
|
-
|
|
27349
|
+
const existingExt = await storage.readArtifact(projectId, extRelPath);
|
|
27350
|
+
if (artifactScope.includes("EXT") && (!existingExt || force || isStaleVsPlan(existingExt))) {
|
|
27140
27351
|
onProgress?.("@activity", `Authoring Advanced Extension Challenge: \`EXT_${lessonCode}.md\`...`, { artifactType: "EXT" });
|
|
27141
27352
|
const extDateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
27142
27353
|
const isVi = targetLang === "vi";
|
|
@@ -27206,7 +27417,7 @@ ${buildHeadingDirective("EXT", slcMarkdown, targetLang)}`;
|
|
|
27206
27417
|
}
|
|
27207
27418
|
);
|
|
27208
27419
|
if (rawExt && !rawExt.startsWith("\u26A0\uFE0F") && rawExt.length >= 200) {
|
|
27209
|
-
await storage.saveArtifact(projectId, extRelPath, rawExt);
|
|
27420
|
+
await storage.saveArtifact(projectId, extRelPath, stampPlanHash(rawExt));
|
|
27210
27421
|
await storage.updateArtifactState(projectId, lessonCode, "EXT", {
|
|
27211
27422
|
state: "completed",
|
|
27212
27423
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|