diff --git a/scripts/selftest.mjs b/scripts/selftest.mjs index 1079d47..2d85da6 100644 --- a/scripts/selftest.mjs +++ b/scripts/selftest.mjs @@ -308,6 +308,60 @@ console.log("⑤-b 習慣記憶(--type habit)"); check("沒有慣例的人格整段不出現", pl.habitBrief("calmsplit") === ""); } +// 自傳章節:長期記憶與心智圖之間的時期層 +console.log("⑤-c 自傳章節(memory/chapters/)"); +{ + check("開一段(to 空著=還在這一段裡)", (() => { + const res = cli(["chapter", "add", "--session", S_HOST, "--name", "做 persona 這段", + "--from", "2026-07-01", "--body", "跟他一起把人格系統做起來的那段日子。"]); + const now = pl.currentChapter("alpha"); + return res.status === 0 && now?.name === pl.slugify("做 persona 這段") && now.open === true; + })(), JSON.stringify(pl.loadChapters("alpha"))); + check("to 沒有值就不寫那一行(不要寫成空欄位)", + !fs.readFileSync(pl.chapterPath("alpha", "做 persona 這段"), "utf8").includes("to:")); + check("現在這一段會注入,而且講明它是時期不是事件", (() => { + const ctx = pl.turnContext("alpha", S_HOST, "最近在忙什麼"); + return ctx.includes("現在這一段") && ctx.includes("做 persona 這段") && + ctx.includes("這是**時期**不是事件"); + })(), pl.turnContext("alpha", S_HOST, "最近在忙什麼").slice(0, 400)); + check("新固化的記憶自動掛在當下那一段", (() => { + cli(["consolidate", "--persona", "alpha", "--session", S_HOST, "--name", "章節歸屬測試", + "--body", "這則應該掛在做 persona 這段。"]); + const meta = pl.longTermEntries("alpha").find((m) => m._name === "章節歸屬測試"); + return meta?.chapter === pl.slugify("做 persona 這段"); + })(), JSON.stringify(pl.longTermEntries("alpha").map((m) => `${m._name}:${m.chapter || "-"}`))); + check("chapterMemories 查得回這一段的記憶", + pl.chapterMemories("alpha", "做 persona 這段").some((m) => m._name === "章節歸屬測試")); + check("--chapter 可以指定掛到別的一段", (() => { + cli(["chapter", "add", "--session", S_HOST, "--name", "在 SAO 那兩年", + "--from", "2022-11-06", "--to", "2024-11-07", "--body", "被關在那裡的兩年。"]); + cli(["consolidate", "--persona", "alpha", "--session", S_HOST, "--name", "第一次到第一層", + "--chapter", "在 SAO 那兩年", "--body", "第一次踏出起始城鎮。"]); + const meta = pl.longTermEntries("alpha").find((m) => m._name === "第一次到第一層"); + return meta?.chapter === pl.slugify("在 SAO 那兩年"); + })()); + check("已收掉的段不會被當成「現在這一段」", + pl.currentChapter("alpha")?.name === pl.slugify("做 persona 這段")); + check("章節按起始日期排序(不是按檔名)", (() => { + const rows = pl.loadChapters("alpha"); + return rows[0].name === pl.slugify("在 SAO 那兩年") && rows.at(-1).open === true; + })(), JSON.stringify(pl.loadChapters("alpha").map((c) => `${c.from}:${c.name}`))); + check("close 之後就沒有進行中的段了(不會退回上一段)", (() => { + cli(["chapter", "close", "--session", S_HOST, "--name", "做 persona 這段", "--at", "2026-08-03"]); + return pl.currentChapter("alpha") === null && pl.chapterBrief("alpha") === ""; + })(), JSON.stringify(pl.loadChapters("alpha"))); + check("chapter list 講得出每段有幾則記憶", (() => { + const out = cli(["chapter", "list", "--session", S_HOST]).stdout; + return out.includes("在 SAO 那兩年") && out.includes("記憶 1 則"); + })(), cli(["chapter", "list", "--session", S_HOST]).stdout); + check("memory/chapters/ 剛好被一個區涵蓋,而且是 Wiki 區(低頻設定)", (() => { + const hit = gt.AREA_KEYS.filter((key) => gt.AREAS[key].paths + .some((p) => (p.endsWith("/") ? "memory/chapters/x.md".startsWith(p) : "memory/chapters/x.md" === p))); + return hit.length === 1 && hit[0] === "wiki"; + })(), JSON.stringify(gt.AREA_KEYS.filter((key) => gt.AREAS[key].paths + .some((p) => (p.endsWith("/") ? "memory/chapters/x.md".startsWith(p) : "memory/chapters/x.md" === p))))); +} + console.log("⑥ 短期 → 長期的轉入條件"); check("有六條成文條件", pl.PROMOTION_RULES.length === 6); check("R1 高顯著度會成為候選",