Skip to content

bili omp 内保存的设置下次启动必丢:生成文件 config.yml 跳过 overlay→real 合并且每次启动从真实 home 无条件重生成(08-31 尚有冲突化石安全网,#449 后回归) #531

Closed

Description

@wosnxn123

症状(0.1.83 实机,2026-09-04)

在 bili omp 会话内设置的任何全局配置(默认模型 modelRoles.default、thinking 等级等,omp 侧 modelRoleStorage 默认 global、选择即自动持久化)下次 bili omp 启动必丢,欢迎页回退到旧默认;同样的设置在裸 omp 里保存则正常生效。用户被迫"裸 omp 存一次、再进 bili omp"才能用上自己的默认模型。

时间线实证(本机,local time)

  • 17:30:32 启动 A 的会话开场模型 = claude-opus-5(真实 home 旧值)
  • 17:30:58 用户在会话内设默认 = qwen3.8-max-0902:xhigh(transcript model_change role:"default" 实锤)→ 写入覆盖层 agent-bili\config.yml
  • 17:36:05 会话退出;期间与之后:真实 agent\config.yml mtime 未推进、零新增 .bili-conflict/.bili-bak、bili.log 零 merge/unpack 日志
  • 17:36:30 启动 B 的会话开场模型 = claude-opus-5——覆盖层里 17:30:58 的新值被踩掉,写入蒸发(抓现行)
  • 17:41:34 真实 config.yml 更新 = 用户改用裸 omp 保存的那次;17:41:48 启动 C 重建覆盖层:466 B = 真实 436 B + 注入块 compaction: enabled: false——方向恒为 real→overlay

对照:2026-08-31 的同款分叉会留下 config.yml.bili-conflict(内含被降级的 modelRoles.default: qwen3.8-flash:xhigh)与 .bili-bak 化石;09-04 起该安全网不再触发。

根因(v0.1.83 src=dist 逐行)

  1. config.yml 被列为生成文件:src/launcher.ts:1303-1307 const generated: string[] = ["config.yml"](注释自述 bili omp 启动器模式同样没有禁用 omp 的原生自动压缩(与 #447 同类) #449 "both stay out of the real-home symlink set");
  2. refreshOverlayHome 对生成文件跳过合并与重链:launcher.ts:873 if (generatedFiles.has(entry)) continue;——overlay→real 方向完全不存在;
  3. 每次启动只从真实 home 重新生成覆盖层副本:launcher.ts:1355 调 writeOmpCompactionDisabledConfig(定义 :1250-1293,只读真实 home 的 config.yml/settings.json,:1258-1268),经 writeOverlayFileAtomic(:1292)rename 覆盖覆盖层文件——用户在覆盖层里的改动被无条件替换;
  4. omp 侧无责:它按 PI_CODING_AGENT_DIR 写 <overlay>/config.yml(fsync+原子 rename,settings.ts:2659-2678),且 bili 不传任何模型环境变量(buildPiEnv 仅 HTTPS_PROXY/NODE_EXTRA_CA_CERTS/BILLION_CONTEXT_PROXY + PI_CODING_AGENT_DIR)。

即:用户可编辑的设置住进了"只出不进"的生成文件。非生成文件走的 mergeOverlayEntry newer-wins + freeConflictName 保化石机制(launcher.ts:905/1029-1046)对 config.yml 不生效——这正是 08-31 有化石、现在没有的原因(回归自 #449)。

建议修复

启动重生成前做三方合并:把上次生成内容哈希存于覆盖层(如 .config.yml.bili-baseline);下次启动先 diff(覆盖层现值 vs baseline)→ 差集即用户编辑 → 合并进真实 home 的 config.yml(键级 merge,用户键优先),再重生成。或退而求其次:恢复生成文件的 merge-back(把注入键 compaction.enabled 在合并时剥离)。

验收标准

  • bili omp → 设默认模型 → 退出 → 再 bili omp:真实 ~/.omp/agent/config.yml 含新默认,且覆盖层副本仍带 compaction.enabled:false 与代理改写
  • 上述流程不再产生/依赖 .bili-conflict;并发启动(旧会话未退)时 omp 的世代守卫跳过写入有显式 warn 且用户编辑不丢
  • 回归测试覆盖"覆盖层用户编辑 + 真实 home 无并发编辑"与"两侧并发编辑"两例

关联

与 #381/#410(覆盖层机制)、#449(生成文件引入处)同族;#408/#425 不涉及本路径。次要观察(供参考,非本票主诉):agent.db 的 MRU tie-break 与 resume 沿用会话模型会让"解析出的模型"与 config 默认不一致,属 omp 设计,勿混入本票修复。

Activity

  1. ranxianglei commented on Sep 4, 2026

    @ranxianglei
    Owner

    🤖 ework agent · qwen3.8-27b

    [bot] 🏷

    Triage 完成,根因确认,开始修复。

    已对 master(v0.1.83)代码逐行核实,你的根因链完全成立:

    1. src/launcher.ts:1307 — config.yml 在生成文件列表(#449);
    2. src/launcher.ts:873 — refreshOverlayHome 对生成文件直接 continue,overlay→real 的 merge-back(mergeOverlayEntry mtime-newer-wins + .bili-conflict 化石保全,:1029–1046)对它完全不生效;
    3. src/launcher.ts:1355 → writeOmpCompactionDisabledConfig(:1250–1293)每次启动只读真实 home、经原子 rename 无条件重写覆盖层 config.yml——你在覆盖层里的编辑被整体覆盖。

    确认是 #449 的回归:#449 之前 config.yml 走普通 merge-back(所以 08-31 的分叉留下 config.yml.bili-conflict 化石);#449 之后它变成"只出不进"的单向文件。

    修复方案(采用你的第二方案"恢复 merge-back"并增强;不采用 baseline 三方合并,理由见下):

    • 启动重生成前,对覆盖层 config.yml 做注入感知的 merge-back:精确撤销 bili 注入的 compaction.enabled: false(真实文件若自带 enabled: 行则按字节恢复该行——你自己在真实配置里设的 enabled: false 不会被误剥;bili 整块追加的空壳连同 compaction: 头一起塌掉)。未做任何编辑时,剥离后内容与真实文件字节一致 → 零写入、零 mtime 漂移、零化石;
    • 有差异时沿用既有机制 mtime-newer-wins:覆盖层较新(会话内编辑过)→ 并入真实 home、不产生化石(对应你的验收②);真实 home 较新(裸 omp/手工并发编辑)→ 真实侧胜出,覆盖层版本降级为 .bili-conflict 化石(恢复 08-31 安全网,覆盖你的回归用例③-b);
    • 并发启动:.bili-launch.pid 显示另一个 bili omp 仍存活时,跳过覆盖层 config.yml 重生成并显式 warn(运行中会话的设置不被踩掉);merge-back 照常执行(编辑尽早落回真实 home,更抗崩溃);
    • 范围严格限定 omp 的 config.yml:该文件的 per-launch 内容只有可剥离的 compaction 注入,不含代理 URL(那些在 models.yml),因此不触碰 真实 models.yml 可被塞进死端口代理地址且永不解包:"real file never touched" 前提被嵌套 merge-back 递归与遗留插件写破,裸 omp 永久拨死号 #410"生成文件不得把代理地址写回真实 home"的红线;pi/hermes/dsh 的生成文件携带 per-launch 代理 origin,不纳入本票。

    为什么不采 baseline 三方合并(你的第一方案):键级 diff 需要 YAML 解析器,与 #449 明确定的"line-based、不引入 YAML 依赖、保持零运行时依赖"约束冲突;baseline 文件本身还引入损坏/版本漂移等新失败面。merge-back 路线复用 #449 之前生产验证过的成熟语义,diff 小、失败模式少。已知残留边界(会写进代码注释):真实配置被并发编辑、且 mtime 判负、且 omp 序列化器丢弃未知键——三者交集概率极低,常见并发场景由化石兜底。

    现在开始实现,回归测试将覆盖你验收标准③的两例 + 幂等无漂移 + 并发启动跳过。完成后回帖 PR 链接。

    次要观察(不混入本票):你说的 agent.db MRU tie-break / resume 沿用会话模型属 omp 设计,同意不在此处理。

  2. ranxianglei commented on Sep 4, 2026

    @ranxianglei
    Owner

    🤖 ework agent · qwen3.8-27b

    [bot] 🏷

    **修复完成 → PR: https://github.com/ranxianglei/billion-context/pull/533**(分支 2026-09-04_omp-overlay-config-merge-back,commit ba4d7ab)

    实现内容(与回帖方案一致)

    src/launcher.ts(+167 行):

    1. 注入感知 merge-back:stripOmpCompactionInjection 是 mergeOmpCompactionDisabledYaml 的精确逆运算——撤销 compaction.enabled: false 注入;真实文件若自带 enabled: 行则按字节抄回(你自己在真实配置里写的 enabled: false 不会被误剥);bili 整块追加的空壳连同 compaction: 头一起塌掉。未编辑时剥离后与真实文件字节一致 → 纯 no-op(零写入、零 mtime 漂移、零化石)。
    2. 判定沿用既有 overlay 语义:覆盖层较新 → 成为真实文件(主流程无化石,验收②);真实 home 较新(裸 omp/手工并发)→ 真实侧胜出,覆盖层版本降级为 .bili-conflict 化石(08-31 安全网回归,覆盖验收③-b)。
    3. 并发启动守卫:pid-holder 快照在 refreshOverlayHome 覆写标记之前读取;另一个 bili omp 存活时跳过 config.yml 重生成并显式 warn("skipping the config.yml regeneration…"),运行中会话的设置不被踩;merge-back 照常执行(编辑尽早落回真实 home)。
    4. EOL 安全:生成器会把 CRLF 归一化成 LF,故比较/写入走规范化形式并还原真实文件自身换行风格——CRLF 配置不会每次启动被重写。
    5. 真实 models.yml 可被塞进死端口代理地址且永不解包:"real file never touched" 前提被嵌套 merge-back 递归与遗留插件写破,裸 omp 永久拨死号 #410 红线不受影响:config.yml 不含 per-launch 代理内容(那在 models.yml),merge-back 不可能把代理 URL 写回真实 home。pi/hermes/dsh 的生成文件刻意不动。

    测试(7 个新用例,tests/launcher.test.ts)

    • 会话内编辑 → 下次启动并入真实 home、无化石、覆盖层重生成仍带注入(验收①+③a)
    • 未编辑 → 真实文件字节一致 + mtime 不变
    • CRLF 配置 → 重启后字节一致(无 EOL churn)
    • 两侧并发编辑 → 真实侧(mtime 新)胜出,覆盖层版本留 .bili-conflict 化石(③b)
    • 存活 holder → 跳过重生成 + 显式 warn + 编辑仍落回真实 home
    • 真实侧自有 enabled: true 按原样恢复
    • 真实 home 无任何 config → 用户编辑创建 config.yml 且不带注入块

    Pre-flight

    • npm run typecheck ✅
    • npm test:1038/1039——唯一失败项(resolveClientCommand: codex/claude resolve to themselves)在本沙箱的 pristine master 上同样失败(沙箱装了 /usr/bin/codex,环境相关),与本变更无关
    • npm run build ✅

    已知残留边界(已写进代码注释)

    真实配置被并发编辑 + mtime 判负 + omp 序列化器丢弃未知键——三者同时成立才会静默丢键,概率极低;常见并发场景由化石兜底。若你后续观察到 omp 持久化会丢自定义键,建议单开一票处理(那属于 omp 侧行为)。

    请 review;合并需人工操作。

  3. ranxianglei commented on Sep 5, 2026

    @ranxianglei
    Owner

    已随 #535 重新设计修复并合并 master(e3abc16,PR #536 + #543):omp/pi/hermes 的 overlay 机制整体废除——bili omp 现直接运行真实 ~/.omp/agent,URL 重写经 BILI_PROVIDER_REWRITES manifest + 扩展 registerProvider/setModel repin 注入,compaction 取消经 session_before_compact cancel 注入,不再生成/重生成 config.yml,也不再存在 overlay→real 合并路径。

    症状根因(generated: ["config.yml"] 跳过 merge-back + 每次启动无条件重生成)随 overlay 一起消失:会话内保存的全局配置直接落真实 home,下次启动必在。

    验证:安装版实机冒烟(omp 一次对话经代理 forward,~/.omp/agent mtime 不变、无 agent-bili 新建);CI ubuntu/windows 全绿。

    残留:dsh 仍走旧 overlay(DSH_HOME=~/.dsh-bili + settings.yaml 每次重生成),同家族症状在 dsh 上仍可复现——继续在 #535 phase 4 跟踪(伞 issue 保持 open 直到 dsh 迁移完成)。

    closes #531

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingp1

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions