Repository navigation
Description
背景
conflictAutoResolveEnabled(#603 第 4 层,提交 7fbd60f / 9b2b2ef)是给人工裁决减负的自动档:相似度 ≥ 阈值的同 scope 对直接保留信息量更大的一条、归档另一条,不进人工队列。
在把这档真正打开之前做了一次审计,结论是现在不该开。三条边界如下,都需要落到代码注释与文档里,否则下一次调整会把它们重新踩掉。
一、阈值论证的数字是早期快照,当前库里这档等于不生效
提交信息与 src/dream/sleep.js 的注释都写着:
live 库 345 条有相似度的历史行里,>=0.95 只有 20 条(5.8%)、>=0.94 是 28 条(8.1%)
2026-11-09 在 live 库 D:\DSH\.dsh\memory\memory.db 重跑相同口径:
| 项 | 实测 |
|---|---|
conflict_pending 全表 |
748 行 |
similarity 非 NULL |
11 行 |
| 其中 ≥0.95 | 0 行 |
| 最高一对 | 0.914 |
| 分档 | ≥0.95=0,0.94–0.95=0,0.90–0.94=1,<0.90=10 |
也就是说按默认阈值 0.95 打开开关,今晚一对都不会被自动裁决——用户看到的会是「什么都没变」,然后怀疑功能坏了。这是最该避免的「假成功」。
另:748 行里 737 行 similarity 为 NULL(62606bc 之前入库的老行),这批在队列排序里沉底,相似度条只能回落到解析 reason 字符串。
二、自动档嵌在 freeze 分支里,不是独立开关
src/dream/sleep.js:432 的 if (config.conflictFreezeEnabled === true) 一直开到 :566,自动裁决(autoEnabled/autoPool/autoResolved)全在这段里。:568 往后的非 freeze 路径只有「跨 scope 停车 + LLM 裁决」,没有自动档。
面板上 conflictFreezeEnabled 与 conflictAutoResolveEnabled 看着是平行开关,实际是父子关系。哪天关掉 freeze,自动档会跟着静默消失,且没有任何提示。
三、自动档零可见,台账记不出赢家
src/dream/sleep.js:555返回的autoResolved/autoFailed在全仓除sleep.js自己与test/scope-candidates.test.js外没有任何消费者;src/dream.js的两处 return(:1675、:1741)只取frozen。conflict_reviewed表有saveConflictReviewed/loadConflictReviewed,但没有任何 API 路由暴露。- 该表 schema 为
(memory_a, memory_b, judged_at, outcome, run_id),不记哪一方赢。而memory_a/b是排序过的,事后想回答「这条为什么被归档」只能去读 loser 正文里的「(已被取代:…)」后缀反推。
四、未标注 scope 被当成同 scope(最重的一条)
isCrossScopePair(src/dream/sleep.js:359):
const isCrossScopePair = (p) =>
scopeKeyOf(p.a.agent_scope) !== scopeKeyOf(p.b.agent_scope)
|| scopeKeyOf(p.a.workspace_scope) !== scopeKeyOf(p.b.workspace_scope);scopeKeyOf(src/scope.js:185)把 NULL / 空串归一成 null,于是 null !== null 为 false —— 「未标注」被判定为「同 scope」。
实测活跃库(archived=0):
| 项 | 实测 |
|---|---|
| 活跃记忆总数 | 8259 |
| 两个 scope 全 NULL | 2710(32.8%) |
agent_scope NULL |
2710 |
workspace_scope NULL |
2714 |
这批的归属是未知,不是相同。按「归属是用户的决定、永不自动裁决」(v0.8.1 #170)这条守卫,它们本来该落在不可自动的范围里,现在却直接进自动池。
一个利好:自动归档是可逆的
src/api.js:1055的 POST/update接archived布尔lib/client.js:6961有{id, archived:false}恢复按钮src/tools.js:626的memory_update支持archived=falselib/client.js:8223有「只看已归档」过滤器
所以不是删数据,风险等级比听上去低。失败也会退回人工队列(src/dream/sleep.js:530-545),不会静默吞。
建议的处理顺序
- 先补可见性:面板能报「本轮自动裁决 N 对」与「自动档失败退回 M 对」,
autoResolved从sleep.js透出到dream.js的 phases 与面板。 - 修 NULL scope 判据:
isCrossScopePair里把「任一未标注」显式判为不可自动(而不是当同 scope),并为这批行给出归属提示。 - 台账补 winner 列:
conflict_reviewed加winner/loser,或至少记winner_id,让事后归因不必反推正文。 - 解除父子绑定:自动档不该依赖
conflictFreezeEnabled,或至少在 freeze 关闭时明确告警。 - 阈值按当前分布重定:开放后再按实测分布调,不要沿用注释里的 345/20。若坚持沿用 0.95,需在注释里注明该数字的来源快照与日期。
真正在减负的其实是 #603 已生效的第 1 层(每轮 ≤60、队列容量 conflictFreezeMaxPending=100),加上 748 条存量已全部清空(未解决 0 行)。自动档是「减峰值」,不是「清队列」。
审计来源:Ephor(dsh-mneme 代码审计)+ Amarre 在 live 库的独立复算,2026-11-09。
Activity
docs(sleep): record the three boundaries of the conflict auto-resolve…
modusensus commentedon Nov 9, 2026
边界已写进代码注释与配置说明,提交
6b7c497(comments only,无行为变化),已随 #614 合入 main(514d238)。#616 的三个待办仍开着,留给真正打开这一档之前处理:
autoResolved/autoFailed透出到 dream phases 与面板isCrossScopePair把「任一 scope 未标注」显式判为不可自动conflict_reviewed补 winner 列自动档默认仍为关(
conflictAutoResolveEnabled = false),本次不动开关。feat(dream): 冲突分诊账本 enforce 档——确定性处置接线(#641 beta 第二阶段)
feat(dream): 冲突分诊账本 enforce 档——确定性处置接线(#641 beta 第二阶段)
feat(dream): 冲突分诊账本 enforce 档——确定性处置接线(#641 beta 第二阶段)
feat(dream): 冲突分诊账本 enforce 档——确定性处置接线(#641 beta 第二阶段)