fix(memory): initialize candidates and enforce lifecycle rollout
Memind CI / Test, build, and release guards (push) Successful in 3m29s

This commit is contained in:
john
2026-07-21 22:55:19 +08:00
parent 0dd9331e5b
commit bfb1f6fea9
8 changed files with 207 additions and 14 deletions
+1
View File
@@ -10,6 +10,7 @@
| [mindspace-remote-page-sync-and-thumbnail.md](./mindspace-remote-page-sync-and-thumbnail.md) | ① remote 模式 public HTML 入库 sync ② storage 缺失时缩略图/读页回退 workspace HTML |
| [page-data-delivery-contract.md](./page-data-delivery-contract.md) | 数据集注册、绑定、真实 page UUID 与交付验收 |
| [h5-session-stream-replay.md](./h5-session-stream-replay.md) | Portal/Goose SSE 游标映射、断线续播与 Finish 终态恢复 |
| [memory-v2-candidate-and-lifecycle.md](./memory-v2-candidate-and-lifecycle.md) | 候选记忆表幂等初始化、Portal fail-open、生命周期 off/canary/active 作用域 |
## 自动化
@@ -0,0 +1,45 @@
# Memory V2 候选表与生命周期灰度守卫
## 已知故障
生产曾出现 `h5_memory_v2_candidates` 不存在:候选记忆写入和后台查询报错,
但 Agent 召回仍在 shadow 模式执行,因此表现为“有召回事件、回答未注入、个人记忆状态降级”。
同时,生命周期 worker 只检查了 worker 开关;`expire()` 没有检查 rollout
作用域。若忘却开关开启,即使 `MEMORY_LIFECYCLE_ROLLOUT_MODE=off`,定时器也可能
归档全量用户的过期记忆。
## 必须保留的行为
1. `MEMORY_CANDIDATE_PERSISTENCE_ENABLED=1` 且 MySQL 可用时,Portal 必须先执行
`ensurePersonalMemoryCandidateSchema()`,再创建候选记忆 store。
2. 候选表 DDL 失败不能阻塞 Portal 聊天启动;Portal 应记录告警并退回 bounded-memory。
3. memind_adm 必须在创建候选 store 前完成同一幂等建表;初始化失败时后台启动失败,
不允许以“页面可用但候选接口持续报错”的半初始化状态运行。
4. 生命周期 rollout 语义必须严格一致:
- `off`:不得执行任何写操作,也不得启动 worker 定时器。
- `canary`:只允许 `MEMORY_LIFECYCLE_ROLLOUT_USER_IDS` 中的用户。
- `active`:才允许无 user scope 的全局任务。
5. `forgetMemory()``expire()` 都必须同时满足功能开关与 rollout 作用域。
## 回归检查
```bash
node --test memory-v2-personal-store.test.mjs memory-v2-lifecycle.test.mjs memory-v2-runtime.test.mjs
npm test
```
memind_adm 同时执行:
```bash
node --test server/personal-memory-candidate-store.test.mjs
npm test
```
灰度上线后还必须验证:候选表存在、候选写入成功、后台可查询,以及一个测试用户完成
“保存 → 候选 → 晋升 → 新会话召回 → 回答注入”的闭环。未通过前不得切换全量 active。
## 回滚
代码可以按标准 runtime/release 回滚;候选表是加法结构,回滚时保留空表或已有数据,
不得为了代码回滚直接删除表。先把候选、生命周期和 Agent 注入模式切回 shadow/off。