feat(記憶與目標): 專案目標三條 + 固化候選不再重算(0.2.1) #17

Merged
admin merged 41 commits from develop into master 2026-08-03 09:15:21 +00:00
Showing only changes of commit 188edcf8b7 - Show all commits
+54
View File
@@ -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 高顯著度會成為候選",