|
|
@@ -3,6 +3,7 @@ import {
|
|
|
PLANNER_MEMO_SYSTEM_PROMPT,
|
|
|
PLANNER_MEMO_USER_TEMPLATE,
|
|
|
buildPlannerUserMessage,
|
|
|
+ buildGoldenOpeningGuidance,
|
|
|
} from "../agents/planner-prompts.js";
|
|
|
|
|
|
describe("PLANNER_MEMO_SYSTEM_PROMPT", () => {
|
|
|
@@ -84,3 +85,83 @@ describe("buildPlannerUserMessage", () => {
|
|
|
expect(out).toContain("是否黄金三章:是");
|
|
|
});
|
|
|
});
|
|
|
+
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+// Phase 6.5 — Golden Opening Guidance prose
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+describe("buildGoldenOpeningGuidance", () => {
|
|
|
+ it("emits zh slot prose for chapter 1 (confront core conflict)", () => {
|
|
|
+ const out = buildGoldenOpeningGuidance(1, "zh");
|
|
|
+ expect(out).toContain("黄金三章规划指引");
|
|
|
+ expect(out).toContain("第 1 章");
|
|
|
+ // Ch1 slot: throw protagonist into core conflict
|
|
|
+ expect(out).toContain("核心冲突");
|
|
|
+ expect(out).toContain("主角出场即面对主线矛盾");
|
|
|
+ // Opening economy
|
|
|
+ expect(out).toContain("场景 ≤ 3");
|
|
|
+ expect(out).toContain("人物 ≤ 3");
|
|
|
+ // Information layering
|
|
|
+ expect(out).toContain("信息分层");
|
|
|
+ });
|
|
|
+
|
|
|
+ it("emits zh slot prose for chapter 2 (demonstrate the edge)", () => {
|
|
|
+ const out = buildGoldenOpeningGuidance(2, "zh");
|
|
|
+ expect(out).toContain("第 2 章");
|
|
|
+ expect(out).toContain("金手指");
|
|
|
+ // Must demand a concrete event, not narration
|
|
|
+ expect(out).toContain("一次具体事件");
|
|
|
+ });
|
|
|
+
|
|
|
+ it("emits zh slot prose for chapter 3 (lock the short-term goal)", () => {
|
|
|
+ const out = buildGoldenOpeningGuidance(3, "zh");
|
|
|
+ expect(out).toContain("第 3 章");
|
|
|
+ expect(out).toContain("短期目标");
|
|
|
+ expect(out).toContain("3-10 章");
|
|
|
+ });
|
|
|
+
|
|
|
+ it("emits en slot prose for chapter 1 with all three slot descriptions", () => {
|
|
|
+ const out = buildGoldenOpeningGuidance(1, "en");
|
|
|
+ expect(out).toContain("Golden Opening Guidance");
|
|
|
+ expect(out).toContain("Chapter 1");
|
|
|
+ expect(out).toContain("core conflict");
|
|
|
+ expect(out).toContain("concrete event");
|
|
|
+ expect(out).toContain("short-term goal");
|
|
|
+ });
|
|
|
+
|
|
|
+ it("returns empty string for ch>=4 in both languages", () => {
|
|
|
+ expect(buildGoldenOpeningGuidance(4, "zh")).toBe("");
|
|
|
+ expect(buildGoldenOpeningGuidance(5, "zh")).toBe("");
|
|
|
+ expect(buildGoldenOpeningGuidance(4, "en")).toBe("");
|
|
|
+ expect(buildGoldenOpeningGuidance(99, "en")).toBe("");
|
|
|
+ });
|
|
|
+
|
|
|
+ it("renders as cohesive prose, not a numbered or bulleted checklist", () => {
|
|
|
+ const zh = buildGoldenOpeningGuidance(1, "zh");
|
|
|
+ // Heading is allowed; body must not contain enumerated lines.
|
|
|
+ expect(zh).not.toMatch(/^\s*1\.\s/m);
|
|
|
+ expect(zh).not.toMatch(/^\s*-\s/m);
|
|
|
+ expect(zh).not.toMatch(/^\s*\*\s/m);
|
|
|
+ });
|
|
|
+
|
|
|
+ it("buildPlannerUserMessage appends guidance for ch<=3 and omits it for ch>=4", () => {
|
|
|
+ const base = {
|
|
|
+ previousChapterEndingExcerpt: "",
|
|
|
+ recentSummaries: "",
|
|
|
+ currentArcProse: "",
|
|
|
+ protagonistMatrixRow: "",
|
|
|
+ opponentRows: "",
|
|
|
+ collaboratorRows: "",
|
|
|
+ relevantThreads: "",
|
|
|
+ isGoldenOpening: false,
|
|
|
+ bookRulesRelevant: "",
|
|
|
+ };
|
|
|
+
|
|
|
+ const ch2 = buildPlannerUserMessage({ ...base, chapterNumber: 2 });
|
|
|
+ expect(ch2).toContain("黄金三章规划指引");
|
|
|
+ expect(ch2).toContain("第 2 章");
|
|
|
+
|
|
|
+ const ch4 = buildPlannerUserMessage({ ...base, chapterNumber: 4 });
|
|
|
+ expect(ch4).not.toContain("黄金三章规划指引");
|
|
|
+ });
|
|
|
+});
|