test(selftest): 補自傳章節的案例,753 → 764

涵蓋開段、to 不寫空欄位、每輪注入、新記憶自動掛當下那段、--chapter 指定別段、
已收掉的段不算「現在」、按起始日期排序、close 後沒有進行中的段、list 講得出
每段幾則記憶、memory/chapters/ 剛好被 Wiki 一個區涵蓋。

最後那條原本寫成讀 pl.AREAS(那是 persona-gitea 的匯出,persona-lib 沒有),
會 ?? {} 之後空轉必過。改成走 selftest 既有的 gt.AREAS/AREA_KEYS。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-03 08:57:11 +00:00
co-authored by Claude Opus 5
parent 0d91e458ae
commit 188edcf8b7
+54
View File
@@ -308,6 +308,60 @@ console.log("⑤-b 習慣記憶(--type habit");
check("沒有慣例的人格整段不出現", pl.habitBrief("calmsplit") === ""); 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("⑥ 短期 → 長期的轉入條件"); console.log("⑥ 短期 → 長期的轉入條件");
check("有六條成文條件", pl.PROMOTION_RULES.length === 6); check("有六條成文條件", pl.PROMOTION_RULES.length === 6);
check("R1 高顯著度會成為候選", check("R1 高顯著度會成為候選",