TL;DR
当前 TelegramSearchBot 的更新集成只用了 Moder.Update 的包格式和外部 updater,可是没有真正使用它的链式更新模型。结果是:发布侧每次只生成一个单 entry 的全量替换包,运行时最多只吃一个 entry,B2 又会把历史链条持续剪掉,最后整个系统退化成“远程全量替换分发器”。
目标不是做极致二进制 diff,而是恢复 Moder.Update 设计时的目标:在有限存储内保留足够长的增量更新链,并通过周期性 cumulative/anchor 包控制链长度和体积。
问题总结
当前偏差
TelegramSearchBot.UpdateBuilder 每次只生成一个 catalog entry
- 该 entry 基本是当前版本的全量替换包,而不是基于上一个版本的 step package
- 运行时没有按 Moder.Update 的完整路径规划语义工作,只是匹配一个可用 entry
push.yml 当前上传完后会把历史包版本 aggressively prune,导致更新链无法长期保留
- 包命名仅包含 target version,无法自然表示多来源区间包
结果
- 失去真正的链式增量更新能力
- 存储策略退化成“最新包 + 少量残留历史版本”
- 运行时无法沿 retained chain 逐步升级
- Moder.Update 的
UpdateChecker / PlanUpdatePath / cumulative preference 设计没有被真正用起来
目标设计
设计原则
-
每个版本默认生成 step package
- 表示
previous -> current
- 只包含变更文件,而不是整个 standalone 目录
-
周期性生成 cumulative/anchor package
- 用于压缩链深度,避免客户端必须连续下载过多 step 包
- 不追求极致 diff,而追求“链够长、包数受控、总体存储可界定”
-
catalog 保留多 entry,支持真实路径规划
latestVersion 指向最新版本
entries 保留 step / cumulative / anchor 形成的有效更新图
minRequiredVersion 与保留窗口同步变化
-
运行时按完整路径顺序应用多个包
- 不是只选一个 entry
- 应恢复 Moder.Update 的链式更新语义
-
清理策略按可达性做,而不是按“只保留最新”做
推荐修正方案
1. Builder 重构
TelegramSearchBot.UpdateBuilder 需要从“单次打包当前目录”升级为“基于前序版本元数据生成更新图 entry”。
Builder 应具备的新能力
- 读取前一版或前几版的 release metadata / catalog
- 比较
previous standalone 与 current standalone
- 生成
previous -> current 的 step package
- 只把变更文件写入包
- 在达到阈值时生成
anchor -> current 的 cumulative package
- 输出多 entry catalog fragment,而不是单 entry catalog
包命名建议
- Step:
packages/update-<from>-to-<to>.zst
- Cumulative:
packages/update-<anchor>-to-<to>-cumulative.zst
这样可以自然支持:
1.0 -> 1.1
1.1 -> 1.2
1.0 -> 1.2 cumulative
不会再被“同一 target version 只能有一个包”限制住。
2. Catalog 模型调整
发布时不再覆盖成只有当前版本的单 entry catalog,而是维护一份合并后的 retained catalog。
Catalog 需要满足
- 保留所有当前支持升级路径所需的 entry
latestVersion 始终指向最新版本
minRequiredVersion 指向保留窗口中最老仍支持自动升级的版本
entries 中同时存在:
- 最近 step 包
- 当前 anchor 窗口对应 cumulative 包
- 必要时保留上一 anchor 窗口
3. Runtime 接入修正
SelfUpdateBootstrap.Windows.cs 需要从自定义单 entry 逻辑改成真正的路径规划和顺序应用。
现状问题
当前逻辑本质上是:
- 拉 catalog
- 找一个当前版本可应用的 entry
- 下载并准备这一个包
这没有发挥 Moder.Update 的完整设计。
目标行为
- 获取 merged catalog
- 基于当前版本和
latestVersion 规划完整 update path
- 顺序下载并应用 path 中的多个包
- 优先选择 cumulative package(由 catalog 决定)
- 对
below minRequiredVersion 明确返回“需要重装”
可以继续保留 bridge -> managed install 的大方向,但 managed install 内的升级语义必须恢复成真正的链式更新。
4. Release Workflow 重构
.github/workflows/push.yml 需要改成“先合并旧状态,再发布新状态,再按可达性清理”。
新发布流程建议
- 读取远端现有
catalog.json
- 下载当前保留窗口所需的 metadata(必要时)
- 构建本次
standalone
- 生成:
previous -> current step package
- 可选
anchor -> current cumulative package
- 合并 catalog
- 先上传新 packages
- 再上传新的
catalog.json 和 moder_update_updater.exe
- 最后 prune 不可达旧包
关键点
- prune 必须基于 retained graph,而不是简单“每个文件只保留最新版本”
- GitHub Release 全量包仍然可以保留,作为人工恢复和回滚手段
- B2 上的更新源应以“自动升级可达性”为第一约束
5. Bridge 与恢复策略
bridge 不应该再承担主增量链的一部分,而是作为 bootstrap / reinstall fallback。
建议规则
- managed install 才是主更新目标
- bridge 主要负责:
- 首次导入 managed install
- below
minRequiredVersion 时的重装入口
- managed install 明显损坏时的恢复入口
- bridge 版本可以独立演进,但不应绑死主更新链的 retention 策略
推荐保留策略
这是建议默认值,后续可再调。
包生成策略
- 每个版本都生成 step package
- 满足以下任一条件时额外生成 cumulative package:
- 自上个 anchor 以来已累计 5 个版本
- 自上个 anchor 以来所有 step 包总大小 > 当前完整包大小的 1.5 倍
保留窗口策略
- 保留:
- 当前 anchor 窗口
- 上一个 anchor 窗口
minRequiredVersion 指向当前仍被支持自动升级的最老 anchor 起点
删除策略
仅删除:
- 不在当前保留窗口内
- 且不属于任何受支持版本到
latestVersion 的可达路径
- 且不再被当前 cumulative 包覆盖策略需要的 package
这样能保证:
- 链长度有上限
- 存储不会无限膨胀
- 自动更新路径始终存在
Ralph Loop Task List
以下按 Observe → Reflect → Plan → Act → Assess 拆分。
Observe
observe-update-surfaces
盘点当前 builder、workflow、catalog、bridge、runtime 的实际行为,并明确以下兼容边界:
%LOCALAPPDATA%\TelegramSearchBot\app managed install 目录不变
moder_update_updater.exe 启动契约不变
- bridge 到 managed install 的入口仍保留
below minRequiredVersion => reinstall required 语义保留
产出: 当前更新面清单 + 兼容性边界文档
Reflect
define-chain-policy
定义链策略与保留策略:
- step 包生成节奏
- cumulative/anchor 包生成阈值
- 包命名规则
minRequiredVersion 更新规则
- prune 的可达性判定规则
产出: 更新图保留策略文档
Plan
redesign-update-builder
重构 TelegramSearchBot.UpdateBuilder:
- 输入上一版本 metadata/catalog
- 输出 changed-file step package
- 可选输出 cumulative package
- 生成多 entry catalog fragment
- 生成稳定区间命名的 package path
产出: 新 builder 设计与实现
redesign-release-workflow
重构 push.yml:
- 拉取旧 catalog
- 生成并合并 retained catalog
- 先上传 packages
- 后上传 catalog / updater
- 按 retained graph prune 无效包
产出: 新发布流水线
Act
integrate-moder-update-runtime
修正运行时接入:
- 使用完整路径规划语义
- 顺序应用 update path 中多个包
- 支持 cumulative package 优先
- 对无路径 / 需重装状态返回明确结果
产出: 真正可走长链的运行时更新逻辑
harden-bridge-and-recovery
加固 bridge 与恢复逻辑:
- bridge 刷新策略
- managed install handoff fallback
- reinstall-required UX
- managed install 损坏时恢复路径
产出: 更稳的桥接与恢复策略
Assess
validate-update-chain
覆盖验证与文档:
- 多步升级测试
- cumulative package preference 测试
- prune 安全性测试
- rollback / updater failure 验证
- 用户文档与运维文档更新
产出: 可验证的长链增量更新方案
成功标准
当以下条件都满足时,说明修正完成:
- 新版本发布后,catalog 中存在多 entry,而不是单 entry
- 从保留窗口内任意受支持旧版本到最新版本都能规划出 update path
- 大多数日常发布只新增一个 step package;必要时新增一个 cumulative package
- B2 清理不会破坏任何受支持版本的自动升级路径
- runtime 可以顺序应用多个 package,而不是只应用一个
minRequiredVersion 以下的版本会被明确要求重装,而不是进入错误路径
备注
这次修正的目标不是引入极致 binary diff,而是把 Moder.Update 原本设计好的“链式增量 + 周期性累计包 + 有界存储”真正用起来。
TL;DR
当前 TelegramSearchBot 的更新集成只用了 Moder.Update 的包格式和外部 updater,可是没有真正使用它的链式更新模型。结果是:发布侧每次只生成一个单 entry 的全量替换包,运行时最多只吃一个 entry,B2 又会把历史链条持续剪掉,最后整个系统退化成“远程全量替换分发器”。
目标不是做极致二进制 diff,而是恢复 Moder.Update 设计时的目标:在有限存储内保留足够长的增量更新链,并通过周期性 cumulative/anchor 包控制链长度和体积。
问题总结
当前偏差
TelegramSearchBot.UpdateBuilder每次只生成一个catalog entrypush.yml当前上传完后会把历史包版本 aggressively prune,导致更新链无法长期保留结果
UpdateChecker/PlanUpdatePath/ cumulative preference 设计没有被真正用起来目标设计
设计原则
每个版本默认生成 step package
previous -> current周期性生成 cumulative/anchor package
catalog 保留多 entry,支持真实路径规划
latestVersion指向最新版本entries保留 step / cumulative / anchor 形成的有效更新图minRequiredVersion与保留窗口同步变化运行时按完整路径顺序应用多个包
清理策略按可达性做,而不是按“只保留最新”做
推荐修正方案
1. Builder 重构
TelegramSearchBot.UpdateBuilder需要从“单次打包当前目录”升级为“基于前序版本元数据生成更新图 entry”。Builder 应具备的新能力
previous standalone与current standaloneprevious -> current的 step packageanchor -> current的 cumulative package包命名建议
packages/update-<from>-to-<to>.zstpackages/update-<anchor>-to-<to>-cumulative.zst这样可以自然支持:
1.0 -> 1.11.1 -> 1.21.0 -> 1.2 cumulative不会再被“同一 target version 只能有一个包”限制住。
2. Catalog 模型调整
发布时不再覆盖成只有当前版本的单 entry catalog,而是维护一份合并后的 retained catalog。
Catalog 需要满足
latestVersion始终指向最新版本minRequiredVersion指向保留窗口中最老仍支持自动升级的版本entries中同时存在:3. Runtime 接入修正
SelfUpdateBootstrap.Windows.cs需要从自定义单 entry 逻辑改成真正的路径规划和顺序应用。现状问题
当前逻辑本质上是:
这没有发挥 Moder.Update 的完整设计。
目标行为
latestVersion规划完整 update pathbelow minRequiredVersion明确返回“需要重装”可以继续保留 bridge -> managed install 的大方向,但 managed install 内的升级语义必须恢复成真正的链式更新。
4. Release Workflow 重构
.github/workflows/push.yml需要改成“先合并旧状态,再发布新状态,再按可达性清理”。新发布流程建议
catalog.jsonstandaloneprevious -> currentstep packageanchor -> currentcumulative packagecatalog.json和moder_update_updater.exe关键点
5. Bridge 与恢复策略
bridge 不应该再承担主增量链的一部分,而是作为 bootstrap / reinstall fallback。
建议规则
minRequiredVersion时的重装入口推荐保留策略
这是建议默认值,后续可再调。
包生成策略
保留窗口策略
minRequiredVersion指向当前仍被支持自动升级的最老 anchor 起点删除策略
仅删除:
latestVersion的可达路径这样能保证:
Ralph Loop Task List
以下按 Observe → Reflect → Plan → Act → Assess 拆分。
Observe
observe-update-surfaces盘点当前 builder、workflow、catalog、bridge、runtime 的实际行为,并明确以下兼容边界:
%LOCALAPPDATA%\TelegramSearchBot\appmanaged install 目录不变moder_update_updater.exe启动契约不变below minRequiredVersion => reinstall required语义保留产出: 当前更新面清单 + 兼容性边界文档
Reflect
define-chain-policy定义链策略与保留策略:
minRequiredVersion更新规则产出: 更新图保留策略文档
Plan
redesign-update-builder重构
TelegramSearchBot.UpdateBuilder:产出: 新 builder 设计与实现
redesign-release-workflow重构
push.yml:产出: 新发布流水线
Act
integrate-moder-update-runtime修正运行时接入:
产出: 真正可走长链的运行时更新逻辑
harden-bridge-and-recovery加固 bridge 与恢复逻辑:
产出: 更稳的桥接与恢复策略
Assess
validate-update-chain覆盖验证与文档:
产出: 可验证的长链增量更新方案
成功标准
当以下条件都满足时,说明修正完成:
minRequiredVersion以下的版本会被明确要求重装,而不是进入错误路径备注
这次修正的目标不是引入极致 binary diff,而是把 Moder.Update 原本设计好的“链式增量 + 周期性累计包 + 有界存储”真正用起来。