Repository navigation
[PR] fix(storage): add field names to drift diagnostics + disable incremental fast path during drift latch (#5677) | fix(storage): drift 诊断补字段名 + 漂移锁存期间停用增量快速路径 (#5677) - #5679
Conversation
…le drift-latched #5677: one session's short assistant slot diverged between the envelope leg and the unified-store leg on every sampled check for days — same id on both sides (text stable), byte length flipping between two values, so the disagreeing byte sat in a non-text field the diagnosis never named. The delta fast path skips slots below its watermark by design, so each heal re-diverged within one sample window and "drift resolved" could never fire: the chronic ESCALATED -> FROZEN loop. Two changes in src/storage/shadow.ts: 1. Diagnosis: the first divergent lastMessages slot now carries the sorted top-level field names whose canonicalized values disagree (names only — values stay out of the log line, same privacy contract as the #3919 fingerprints). Rendered as an optional "; fields: a,b" segment appended to the existing DIGEST DRIFT line; the line prefix is byte-identical for existing greps. 2. Convergence: while a session is drift-latched the trusted-prefix delta fast path is suspended (sync and async paths) and every save takes the exact full path, keeping both digest legs same-generation at sample time. The next sample then passes, the latch releases and the delta resumes — a bounded cost paid only by sessions whose views are already proven divergent; healthy sessions are untouched. Tests: tests/issue5678-drift-field-diag.test.ts pins the field naming (pure describeViewDivergence cases plus the end-to-end sampled drift, values-stay-out asserted) and the latch lifecycle (no delta ingest while latched, permanent divergence keeps drifting, a heal + clean sample re-admits, the delta resumes).
📦 Built Package ArtifactBranch: Latest build — install this exact versionnpm install -g billion-context@0.0.8919-pr.5679.7e57a97This exact version is immutable and reproducible. The rolling Recent builds (commit → version)
Download artifact
tar xzf billion-context-pr5679.tgz
npm install -g packageInstall instructions are refreshed on each push. |
(ai: vllm-qwen/qwen3.8-27b) 标签: 查重: 不重复。原始症状家族条目是 #5677(用户报告),本 PR 直接修它;Refs 里的 #4392(已关闭但家族复发)、#3919(指纹契约)、#5408(冻结可见性)、#4614(生产者类命名)都是相关背景,非并列重复立项。 PR review 八问
测试有效性真测试、有牙。纯函数层五类用例(额外字段 / 值变更 / 多字段排序 / 完全一致 / 非对象槽位 bail)+ 值不落日志断言;端到端 CAS 篡改存储槽位 → 采样报出 一处低 severity 观察(不拦合并)
CIhead 结论:方向对(修的是根因机制,不是日志表象)、代码正确、测试扎实、diff 干净(2 文件)、typecheck 绿、定向本地全绿、CI 暂无红。无阻塞项,待 CI |
…async-lane latch gate Review-lane fixes on top of the PR head (test-only, no production code): - Rename tests/issue5678-drift-field-diag.test.ts -> issue5677-...: 5678 is an internal e-work mirror number; the regression belongs to GitHub #5677, and every sibling suite is named by its GitHub issue number. - Add the missing pin for the async lane: while drift-latched, the backend-queue twin must also suspend the delta fast path (the PR's own e2e covers only the sync record() path). Positive control first (unlatched append rides the delta), then a manual latch (#5412 pattern) proves the latched save lands via the exact full path.
|
(ai: vllm-qwen/qwen3.8-27b) 已顺手修:测试文件按 GitHub issue 号重命名(issue5678→issue5677,5678 是内部镜像号)+ 补异步车道锁存门回归测试(PR 自身 e2e 只覆盖同步路径;新测试先做未锁存正对照再手动锁存,钉住 enqueueAsyncRecord 路径上 delta 停用、保存走精确 full 路径)+ commit 4de5cd3(纯测试改动,本地 8/8 绿) |
|
(ai: vllm-qwen/qwen3.8-27b)
APPROVE — 独立全量审核完成(diff 逐行通读 + 同步/异步两条代码路径追踪 + 本地定向验证)。 1. 是什么问题:unified 存储已证明漂移的会话有两个缺口——① 诊断只说得出「diverged: lastMessages」+ 槽位指纹,说不出槽位里哪个字段不一致(v0.1.212 用户会话 id 稳定、长度 148↔149 摆动,字段名几天不可见);② 漂移锁存后增量快速路径仍信任已被证伪的前缀,宿主每轮改写水位线以下的槽位时 ≤20 次保存内必然再漂移,streak 累积 → ESCALATED → FROZEN,无自愈出口。 2. 什么原因导致的:① 诊断层只做整槽 digest 比较,没有字段级分解;② 机制层:full save 重盖水位线后,delta 按设计跳过水位线以下的槽位(这本身正确),于是「锁存 ⇒ 前缀不可信」必须同时蕴含「锁存 ⇒ delta 不得替前缀背书」——原设计缺了这条,收敛环上有个洞。 3. 影响范围与严重程度:仅影响 unified 存储已证明漂移的会话(少数);修复前此类会话必然走到 FROZEN(envelope 写入整体冻结),p2;正常会话路径逐字节不变,零影响。 4. 怎么修复的:① 首个分歧槽位按 canonicalize 分解顶层字段,只列不一致字段的名字(排序、值永不入日志——与 #3919 指纹同一隐私契约);② 同步(:499)与异步(:1171)两条车道都加 5. 是否有回归问题:逐项审过既有行为,无破坏——
6. 是否破坏缓存:否。未触碰前缀缓存/prompt cache 路径;watermark 机制本身未变(锁存期停用 delta 正是修复目标);存储去重键不受影响。 7. 是否改了设计方向:没有。在既有 latch/sampling/watermark 架构内补上「锁存期 delta 信任假设」这一洞,并增强可观测性。 8. 是否引入新配置:没有。 测试:纯函数层覆盖额外字段/值变更/多字段排序/无漂移/非对象槽位 bail-out/值不入日志;e2e 覆盖 tamper → 诊断点名 一个预存在观察(非本 PR 引入,不阻塞):异步车道的漂移日志行(shadow.ts:1227)自 #3919 起就没带诊断信息(同步 :605 带),因此字段名目前只会出现在同步车道日志里——是否对齐由 owner 定夺。 |
[bot] 🏷 PR #5679 审核完成 — APPROVE(已提交)
|
|
(ai: vllm-qwen/qwen3.8-27b)
APPROVE(第二轮复审,head 相对上轮已审 head(
该增量不触碰任何生产代码,首轮全量审核的结论(机制追踪、双车道门控、隐私契约、回归逐项清点——见上一条八问评论)全部继续有效;新 head CI 全绿(30 checks,0 failed)。verdict 不变:可合,等人工合并。 |
[bot] 🏷 第二轮复审完成(head |
…h conflict Union of intent: keep the #5677 drift-latch gate on the sync delta fast path (deltaApplied = !driftedSessions.has(id) && ingestDelta(...)) AND master's #5744 wall-clock guard assignment (ingestPath = "full") inside the full-fallback block. Async-lane latch gate (#5677) auto-merged intact.
(ai: vllm-qwen/qwen3.8-27b) 一句话:当前唯一红项 本轮做了什么(全部实测):
结构化结论(八问):
选项(默认 A):
按 lane 规则停手,等待你的选择。 |
(ai: vllm-qwen/qwen3.8-27b) 红项已转绿,无需再改代码。 复核实时状态(镜像缓存刷新于 11:48Z):head 本轮动作:
一个提示(不在本 wake 范围内处理): 判定:red-CI 阻断已解除,PR 可进入正常的审批/合并流程(合并仍由人工操作)。 |
What & why
#5677 报告了一个会话的存储双副本(JSON 信封 vs SQLite 统一库)里同一条短 assistant 消息每次采样校验都对不上、持续数天:两侧 id 相同(文本稳定)、字节长度在两个值之间翻转 —— 分歧落在某个非文本字段上,而现有诊断只报 id/role/长度/sha8,永远看不到是哪一个字段。同时增量快速路径按设计跳过水位线以下的槽位:宿主每轮改写该槽位 → 每次修复后 ≤20 次保存内再次分歧 → 「drift resolved」永远无法触发 → ESCALATED→FROZEN 慢性循环(报警文本点名的 #4614 类生产者)。
两处改动(
src/storage/shadow.ts):; fields: a,b段;行前缀逐字节不变,既有 grep 不受影响。Behavior changes(披露)
; fields: …段(无分歧字段时不出现在行内),前缀逐字节不变。Tests
新增
tests/issue5678-drift-field-diag.test.ts:fields: id,text→ 锁存窗口内 deltaIngests 恒定(增量确已停用)→ 永久分歧持续报漂 → 修复字节后干净采样解锁 → 增量恢复。本地定向验证:本文件 + issue3919/issue4323/issue5408/issue5412/issue4986/storage-ingest-watermark/storage-unified/unified-acp-invariants/snapshot-shadow/unified-remap-collision 全绿;typecheck 干净。(issue3157-persist-durability 在本机干净树上同样失败——预存环境问题,与本次改动无关。)全量回归以 CI 为准。
Refs #5677(同一症状家族的历史条目:#4392 已关闭但家族复发、#3919 指纹契约、#5408 冻结可见性、#4614 生产者类命名)