Files
deepseek-harness/.agents/notes/implemented/bug-fix/2026-08-13-bounded-cold-blank-verification.zh.md
2026-08-13 14:04:16 +08:00

38 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Agent Note: 有界验证冷空白会话
Status: implemented
[English](2026-08-13-bounded-cold-blank-verification.md) | 中文
## Problem
Web 会话树会隐藏空白 Session并把当前选中的空白项复用为 New Session。已附加 Session 可以从内存事件日志派生空白状态,但 `session.list` 通常不会加载每一份冷日志。把所有已物化的冷 Session 都视为非空,会暴露旧版本留下的空 Session反过来把 projection cache 中的 `blank: true` 当成当前事实,则可能在日志已经前进而 fail-soft cache 仍然陈旧时隐藏真实对话。
同一份冷列表还曾用 JSONL 工件的 mtime 作为 `updatedAt`。打开 Session 会追加 `session/end-seed`,因此即使没有真人 prompt单纯拾起也会刷新 mtime并把该 Session 提升到最近使用的对话之前。
## Decision
`dsh-host-apiproxy` 注册 `sessionListMetadata` 投影,其中包含 `blank``lastPromptAt`。已附加摘要直接用同一组函数折叠实时日志。`blank` 只在 `turn/start` 时从 true 单调变为 false`lastPromptAt` 只在来源 kind 为 `user``user/message` 上更新。
冷摘要信任缓存的 `blank: false`,因为已包含 `turn/start` 的 checkpoint 前缀会始终保持非空。缓存的 `blank: true` 和 cache miss 都无法证明当前日志为空。当 persistence 通过 `locate()` 暴露物理工件,且其大小不超过 `coldBlankProbeMaxBytes`(默认每个 Session 1 KiB网关调用 `readFrom(id, 0)`,验证已存前缀是否含有 `turn/start`。超过上限的文件、不提供位置的后端、已消失的工件和读取失败都产生 `blank: false`,让 Session 保持可见。
`updatedAt``createdAt``lastPromptAt` 中较晚者。因此冷 cache miss 或陈旧 checkpoint 只会让 Session 排得偏旧,而不会因无关的文件写入被提升。有界 blank 读取不用于补齐缺失的最近时间元数据。
## Alternatives considered
**信任缓存的 `blank: true`。** 拒绝,因为 projection cache 有意允许持久日志前进到 checkpoint 之后。首个 `turn/start` 之后若发生崩溃或 fail-soft 写入失败,真实对话就会被隐藏,客户端还可能把它复用为 New Session。
**读取每一份冷日志。** 拒绝,因为列表延迟与 I/O 会随所有已存对话的总字节数增长。物理大小上限只针对能够低成本核验的小型历史工件,更大的未知项则向保持可见降级。
**把空白状态与最近时间存入权威 persistence index。** 暂缓,因为 JSONL 的首行不可变需要增加带有顺序写入要求的第二份持久工件SQLite 则需要 schema 字段。更广泛的精确索引设计仍由[最后活动提案](../../proposed/architecture/2026-07-29-durable-last-activity-index.md)负责。
**继续按 mtime 排序 JSONL。** 拒绝,因为 mtime 记录包括拾起边界在内的每一次工件写入,而非最近真人 prompt其错误方向会把未经操作的 Session 提升到列表开头。
## Consequences
既有的小型空白 JSONL 工件无需依赖 projection cache 是否存在即可被隐藏,陈旧 cache 也无法隐藏已存的 `turn/start`。对于 cache 尚不能证明非空,且物理大小在配置上限内的每个 Session冷列表可能读取其工件。对默认交付的 Zstandard JSONL 后端,该上限作用于压缩后的字节数。
超过上限的空白工件,以及来自不提供位置的后端的空白 Session 会保持可见。缺失或延迟的最近时间 cache 会回退到 `createdAt`。这些都是保守降级UI 可能多显示一条空记录,或把 Session 排得偏低,但不会隐藏真实对话,也不会因为单纯打开而把会话提升到前面。
网关自有投影是网关 fiber 的 effect卸载网关会移除该 key。单元覆盖固定了临界大小探测、拒绝陈旧 true、复用单调 false、回退方向、真人 prompt 最近时间和 fiber 销毁。无密钥 Web snapshot 会启动发行版的压缩 JSONL 组合,在没有 cache row 的情况下播种一份小型冷空白工件,并验证侧栏不展示它。