test(selftest): 補習慣記憶的案例,745 → 753

涵蓋型別合法、front matter 寫對、每輪注入、recall 找得到、上限兩條、
糊掉的不注入(改 salience/strength/last_seen 讓它掉到 fuzzy,測完還原)、
沒有慣例整段不出現。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-03 08:49:04 +00:00
co-authored by Claude Opus 5
parent 10299e18ef
commit bc80c38bf4
+43
View File
@@ -265,6 +265,49 @@ check("情緒事件寫進 journal", pl.readJsonl(pl.journalPath("alpha")).some((
fs.readFileSync(catFile, "utf8").includes("換成別則了。"));
}
// 慣例:程序性記憶。其餘型別都是「記得的事」,慣例是「每次都這樣做的事」
console.log("⑤-b 習慣記憶(--type habit");
{
check("habit 是合法型別", cli(["consolidate", "--persona", "alpha", "--session", S_HOST,
"--name", "不說再見", "--type", "habit", "--salience", "60",
"--body", "我們之間結束對話不說再見,直接停。"]).status === 0);
check("寫進去的型別真的是 habit", (() => {
const meta = pl.longTermEntries("alpha").find((m) => m._name === "不說再見");
return meta?.type === "habit";
})(), JSON.stringify(pl.longTermEntries("alpha").map((m) => `${m._name}:${m.type}`)));
check("慣例每輪注入,不必被問到(照做,不用提起它)", (() => {
const ctx = pl.turnContext("alpha", S_HOST, "今天先這樣");
return ctx.includes("我們之間的慣例") && ctx.includes("不說再見") && ctx.includes("照做,不用提起它");
})(), pl.turnContext("alpha", S_HOST, "今天先這樣").slice(0, 400));
check("慣例是背景不是話題(講出來就變成在討論它)",
pl.habitBrief("alpha").includes("慣例是背景不是話題"));
check("recall 也找得到(型別不影響檢索)",
pl.recall("alpha", "再見", 5).some((r) => r._name === "不說再見"),
JSON.stringify(pl.recall("alpha", "再見", 5).map((r) => r._name)));
check("一輪最多兩條(慣例是背景,不是清單)", (() => {
for (const n of ["先講結論", "不催回覆", "週五不談公事"]) {
cli(["consolidate", "--persona", "alpha", "--session", S_HOST, "--name", n,
"--type", "habit", "--salience", "60", "--body", `我們之間${n}`]);
}
return pl.standingHabits("alpha").length === pl.HABIT_INJECT_MAX;
})(), JSON.stringify(pl.standingHabits("alpha")));
check("糊掉的慣例不注入(想不起來的慣例就不是慣例)", (() => {
const file = path.join(pl.longTermDir("alpha"), "不說再見.md");
const raw = fs.readFileSync(file, "utf8");
// 讓它很舊、強度很低 → 掉到 fuzzy
fs.writeFileSync(file, raw
.replace(/^salience:.*$/m, "salience: 20")
.replace(/^strength:.*$/m, "strength: 1")
.replace(/^last_seen:.*$/m, "last_seen: 2020-01-01T00:00:00.000Z"));
const meta = pl.longTermEntries("alpha").find((m) => m._name === "不說再見");
const faded = pl.memoryStrength(meta).state === "fuzzy";
const gone = !pl.standingHabits("alpha", { limit: 9 }).some((h) => h.name === "不說再見");
fs.writeFileSync(file, raw);
return faded && gone;
})());
check("沒有慣例的人格整段不出現", pl.habitBrief("calmsplit") === "");
}
console.log("⑥ 短期 → 長期的轉入條件");
check("有六條成文條件", pl.PROMOTION_RULES.length === 6);
check("R1 高顯著度會成為候選",