feat(memory): 记忆整理**应用路径 + 调度修复 + 手动真跑**(TIDY-1,主人投诉的根因)
主人实测:memory_items 1191 行、候选 77 条全 pending、memory_tidy_runs 仅 1 行、两晚定时没跑。 根因两条(都在这片里修掉): ① **以前根本没有执行面** —— 只有只读试算,候选永远 pending ② **调度被重启饿死** —— interval=24h 的 ticker 从进程启动算起,两天重启十几次 ⇒ 24h 永远走不完 - 应用路径:逐条 / 按 run 批量(默认 100、硬顶 500、超限 truncated)/ revert;**软合并** (status + merged_into/merged_from/merge_count,**绝不改 content、行不删**)+ 审计 (memory.tidy.apply / memory.tidy.revert)+ **幂等**(候选状态条件更新 + 行 status 闸两道) - 调度:改成**到点即跑**(catchUp:无历史+窗口内 / 距上次 run ≥ interval / 窗口起点晚于上次 run), 「上次 run」以 memory_tidy_runs 为**持久事实**;注入时钟用例证明**重启不饿死** - 手动真跑端点 POST /admin/memories/tidy/run(同档鉴权 + 审计 + 回执:新增/合并/软删/耗时) - 候选表加 run_id 列 + 索引(前滚;顺序=建表→补列→建索引,否则既有库会 no such column) - 3 个变异自检(删 catchUp / 去掉两道 status 闸 / truncated 恒 false) ⚠️ 生产库零动作:1191 行真跑与 77 条候选应用**未执行**(按红线由主人确认后主会话执行) ⚠️ 契约段在报告里(candidates 列表/apply/revert/tidy.run),Docs/ 待登记
This commit is contained in:
1 parent
57d0e8c543
commit
454ddb0b70
18 files changed
+3181
-71
No files matched your search
@@ -83,16 +83,23 @@ func TestTidyServiceNextDelay(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestTidyServiceStartDoesNotRunImmediately 锁定与 backthink 的**有意差异**:
|
||||
// 整理不做启动即跑,进程重启不会触发一次全库扫描。
|
||||
func TestTidyServiceStartDoesNotRunImmediately(t *testing.T) {
|
||||
// TestTidyServiceStartOutsideQuietWindowDoesNotRunImmediately 锁定补跑判据的
|
||||
// 一条边界(切片 TIDY-1):**无历史**且启动在安静窗**之外**时不做启动即跑 ——
|
||||
// 新装的库不该在任意时刻烧一次全库向量化扫描,等窗口起点那一轮即可。
|
||||
// 时钟是注入的固定值,因此这条断言与用例真的跑在几点无关(旧实现用
|
||||
// time.Now,在 23:00–07:00 之间跑就会抖动)。
|
||||
func TestTidyServiceStartOutsideQuietWindowDoesNotRunImmediately(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
store := newTestStore(t)
|
||||
seedTidyItem(t, store, habitItem(fixtureHabitShort, tidyTestTime(t), 0.9))
|
||||
|
||||
svc := newTidyServiceForTest(t, store, tidyTestCfg(0.80), nil, time.Now, nil)
|
||||
clock := &schedClock{at: time.Date(2026, 10, 7, 15, 0, 0, 0, time.Local)}
|
||||
probe := newTidyWaitProbe(clock, 0) // 绝不真的等到窗口起点
|
||||
svc := newTidyServiceForTest(t, store, tidyTestCfg(0.80), nil, clock.now, nil)
|
||||
svc.wait = probe.wait
|
||||
svc.Start(ctx)
|
||||
probe.awaitCalls(t, 1)
|
||||
if err := svc.Stop(ctx); err != nil {
|
||||
t.Fatalf("Stop: %v", err)
|
||||
}
|
||||
@@ -101,7 +108,7 @@ func TestTidyServiceStartDoesNotRunImmediately(t *testing.T) {
|
||||
t.Fatalf("ListTidyRuns: %v", err)
|
||||
}
|
||||
if len(runs) != 0 {
|
||||
t.Fatalf("启动即跑了整理:%+v", runs)
|
||||
t.Fatalf("窗口外无历史时不该启动即跑:%+v", runs)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user