fix(consolidate): --forget 套上短期記憶的保護;長期記憶撞名不再靜默覆蓋
兩個資料遺失路徑: ① `--forget N` 無條件刪掉 salience < N 的全部短期記憶,完全不套 shortTermProtected() (salience ≥ 80/intent=commit/24 小時內)。prune 有這層保護,這裡沒有。 實測 `--forget 200` 把一筆 intent=commit、salience 95、剛寫入的承諾一起刪掉, 而 R4 明文寫「不可遺忘」。改走新的 forgetShortTerm(),跟 prune 同一層保護, 並回報「有幾筆低於門檻但受保護、沒有刪」。 ② slugify() 把標點吃掉:「我的貓」「我的貓?」「我的貓!!」全都落在 `我的貓.md`, writeText 直接覆蓋,只保留舊檔的 first_seen/recall_count,內文靜默被換掉。 front matter 新增 `title:`(原本的 --name),撞名時 die 並給一個沒被占用的檔名 (`--name 我的貓-2`);真的要覆蓋同一則就加 --force。 selftest +11:--forget 不刪承諾/界線/今天的紀錄但該刪的照刪、撞名被擋、 舊內文原封不動、訊息給得出替代檔名、同名再寫是更新、title 有寫入、--force 仍可覆蓋。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+25
-1
@@ -1458,7 +1458,7 @@ export function rememberShort(slug, entry) {
|
||||
}
|
||||
|
||||
/** 這一則短期記憶能不能被容量壓力清掉(承諾與界線永遠不能)。 */
|
||||
function shortTermProtected(row, now = Date.now()) {
|
||||
export function shortTermProtected(row, now = Date.now()) {
|
||||
if (Number(row?.salience || 0) >= SHORT_TERM_PROTECT_SALIENCE) return true;
|
||||
if (String(row?.intent || "").includes("commit")) return true;
|
||||
const age = now - (parseIso(row?.ts)?.getTime() ?? now);
|
||||
@@ -1512,6 +1512,30 @@ export function pruneShortTerm(slug) {
|
||||
return pruneShortTermDetail(slug).kept;
|
||||
}
|
||||
|
||||
/**
|
||||
* 固化完之後主動淘汰顯著度低於 `minSalience` 的短期記憶(`consolidate --forget`)。
|
||||
*
|
||||
* **跟 prune 套同一層保護**:顯著度 ≥ 80、`intent=commit`、24 小時內的紀錄一則都不動。
|
||||
* 以前這裡是無條件 filter,`--forget 200` 會把一筆剛寫入、salience 95 的承諾一起刪掉,
|
||||
* 而 R4 明文寫「不可遺忘」——固化的動作不該變成遺忘承諾的路。
|
||||
*/
|
||||
export function forgetShortTerm(slug, minSalience) {
|
||||
const floor = Number(minSalience);
|
||||
if (!Number.isFinite(floor)) return { kept: 0, dropped: 0, protected: 0 };
|
||||
const now = Date.now();
|
||||
let saved = 0;
|
||||
const { rows, kept } = rewriteJsonl(shortTermPath(slug), (all) =>
|
||||
all.filter((r) => {
|
||||
if (Number(r.salience || 0) >= floor) return true;
|
||||
if (shortTermProtected(r, now)) {
|
||||
saved += 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
return { kept: kept.length, dropped: rows.length - kept.length, protected: saved };
|
||||
}
|
||||
|
||||
export const recentShort = (slug, limit = 8) => readJsonl(shortTermPath(slug), limit);
|
||||
|
||||
export function parseFrontMatter(text) {
|
||||
|
||||
Reference in New Issue
Block a user