返回 DeepSeek-Reasonix
manual-session-compatibility.md
根目录 / docs / testing / manual-session-compatibility.md
1 # Manual session rollback: historical data compatibility
2
3 本报告记录 #10469 行为回滚的增补验证。所有数据均为隔离目录内的合成数据;没有读取或
4 修改用户真实会话,没有安装旧客户端到系统应用目录,也没有发布版本。
5
6 代码基线为 `0d7a103fdd6f`;另在隔离 worktree 将回滚补丁叠加到已抓取的
7 `origin/main-v2`(`f26283afb2f5`)验证。主工作区没有合并或重置到该分支。
8 [机器可读版本与 PR 清单](manual-session-compatibility-sources.json) 固定了 9 个发布标签
9 及 115 条影响相关所有者的第一父链变更。清单用于定位测试边界,不代表逐个 PR 的所有功能均做了全平台验收。
10 [已执行结果与最终包摘要](manual-session-compatibility-results.json) 记录通过的测试组、耗时、
11 日志摘要以及最终 Electron、Go host 和 app.asar 的 SHA-256。
12 后续[提交竞态检查](manual-session-bug-review.md) 修复了三类前端问题;该轮重新构建与原生检查
13 单独记录于结果中的 `submissionRaceReview`,不与本轮历史矩阵的构建摘要混用。
14
15 ## 真实版本边界
16
17 “Desktop Session v5”、注册表 schema、会话逻辑 schema、物理 storage revision 是不同维度,
18 不能仅凭版本号或“都是 v3”判定可降级。
19
20 | 发布版本 | 精确提交 | 历史会话数据 | 注册表 / 持久化草稿 |
21 | --- | --- | --- | --- |
22 | 1.38.3 | fa018e410926 | JSONL + schema-2 事件日志 | 无新注册表 / 无草稿库 |
23 | 1.38.4 | 680e00e5baaf | JSONL + schema-2 事件日志 | 无新注册表 / 无草稿库 |
24 | 1.38.5 | 95d02e52bd99 | JSONL + schema-2 事件日志 | 无新注册表 / 无草稿库 |
25 | 1.38.6 | e2145b031dee | JSONL + schema-2 事件日志 | 无新注册表 / 无草稿库 |
26 | 1.38.7 | 036c7c50c5c1 | JSONL + schema-2 事件日志 | 无新注册表 / 无草稿库 |
27 | 1.38.8 | 7278072720a2 | 旧历史 + framed storage revision 1;没有 recovery storage identity | 无新注册表 / 无草稿库 |
28 | 1.38.9 | dc915ab97bfd | 旧历史 + revision 1 + Desktop 独立身份 header | 注册表 v1 / 无草稿库 |
29 | 1.38.10 | 366e0eb6b86b | 旧历史 + revision 2 + Desktop header | 注册表 v3 / 无草稿库 |
30 | 1.38.11 | 11f9705f0838 | 旧历史 + revision 3 + Desktop header | 注册表 v3 / 草稿 schema 4、snapshot 5 |
31
32 版本依据标签的真实代码与编码器,不能把 main-v2 上的提交日期当作正式标签已经包含该功能。
33 #10469 的合并提交 `e8762acc9` 和 #10572 的实现提交 `54826e46a` 另有独立草稿夹具,
34 覆盖只使用过 main-v2 中间构建的用户;前者冻结 snapshot 4,后者为 snapshot 5。
35 schema 1–3 没有对应上述正式标签,使用按既有迁移定义构造的结构夹具,明确不冒充历史发布产物。
36
37 ### 回退边界的实测修正
38
39 | 数据 | 旧数据升级 | 新写入后的旧读取器 | 结论 |
40 | --- | --- | --- | --- |
41 | 注册表 v1 → v3 | 保留身份、预留操作及未知字段 | 1.38.9 拒绝 v3 | 不支持回退到 1.38.9 |
42 | 注册表 v3 | 1.38.10/11 可读写,未知字段往返保留 | 真实旧读取器成功追加,再升级仍存在 | 仅证明注册表兼容 |
43 | 会话 revision 1/2 → 3 | 旧源不改写;新目标可读并继续写入 | 1.38.10 读取器只支持到 revision 2 | 不支持新版会话整体回退到 1.38.10 |
44 | 会话 revision 3 | 1.38.11 与当前版可读 | 真实 1.38.11 程序读取新版数据并追加消息 | 再升级保留两侧历史 |
45 | session-ui-v1.sqlite | 独立输入库 | 真实旧程序不改动文件 | 旧版继续聊天后,再升级必须核实过时输入 |
46 | 未知未来 schema/revision | 明确错误,原文件保留 | 不写旧 schema 覆盖 | 不静默降级 |
47
48 物理 revision 3 来自 [#10545](https://github.com/esengine/DeepSeek-Reasonix/pull/10545)
49 中的 `e7a5b149e`,并非本次回滚引入。1.38.10 的负向测试要求返回真实
50 `ErrUnsupportedVersion`,且会话权威文件和独立输入库均不改变;不能把拒绝读取计为可用降级。
51
52 ## 新增测试与夹具
53
54 `scripts/generate-session-history-fixtures.py` 在临时目录提取每个标签的真实 Go 模块,
55 编译原版 agent/session 写入器并生成数据。持久化编码、事件校验、framing、内容引用均由原代码完成。
56 仅将进程 writer identity 与 writer announcement hostname 合成化;hostname 替换保持字节长度,
57 不破坏事件索引偏移。每套 `SOURCE.json` 保存标签提交及全部文件 SHA-256。
58
59 | 测试 | 数据和断言 |
60 | --- | --- |
61 | `TestTaggedHistory138*`(四个分组,共享矩阵实现) | 9 版本 × 全局/项目;39 个源会话 × 2 个作用域;每组重复打开 3 次。逐字段比较全部 provider 消息、图片、reasoning、工具调用/结果及大内容;17 条一页遍历,验证全部消息可达且无重复;身份和新输入不因重启变化,旧源不改写 |
62 | 同上:复杂上下文 | 145 条消息、70 次后续问答、长工具结果、Unicode/空格/#/% 路径、独立分支、未完成的本地回复、canonical 未结束 turn、暂停 Goal 和未知 Goal 字段;压缩后模型摘要与完整展示历史分别保留 |
63 | `TestTaggedFormalSessionsKeepHeaderAndProviderHistory` | 1.38.9/10/11 原版 Service 创建的正式 header 会话;原身份、CWD、全部内容和新版追加消息在重启后保留 |
64 | `TestTaggedPreviousWriterRoundtrip` | 新版写入 → 真正编译的 1.38.10/11 程序读取/追加 → 新版读取;1.38.10 安全拒绝,1.38.11 保留历史并将旧输入标记为待核实,禁止直接提交 |
65 | `TestTaggedDraftFixturePreservesAllRecoveryIdentities` | #10469、#10572、1.38.11 各 13 类记录;冻结设置、未知快照字段、仅设置草稿、缺失附件、冲突、已转换及所有提交阶段身份保持 |
66 | `TestTaggedDraftRestartReconcilesWithoutReplay` | 每套旧草稿连续 3 次启动核对;reserved/starting → resume_required;dispatching/shell/failed → dispatch_unknown;accepted 只转换;取消释放保持原身份;无新 Session、无运行时、无自动执行 |
67 | `TestEveryPreviousDraftSchemaPreservesWALAndOpaqueRecords` | schema 1、2、3 的内容、设置、冲突、恢复选择、操作、未知表,以及仅存在已提交 WAL 中的数据,迁移及一致性备份均保留;重复启动不替换旧备份 |
68 | `TestUnrecognizedDraftDatabaseIsNeverInitializedOrRewritten` | 负版本、已有表但版本 0、未来版本;重复打开都报结构化不支持,文件逐字节不变 |
69 | `TestTaggedDamagedHistoryIsolatesFailureAndRetainsRecoveryEvidence` | 1.38.8–11 的未来 revision、截断 commit;坏记录不发布,正常兄弟会话继续可用;修复后不复制身份、原源不变;旧预留操作的源指纹变化保持冲突证据 |
70 | `TestImportWithoutRecoveryIdentityPublishesReadableHistory` | 导入缺少 recovery identity 的原身份/改名身份两条路径;未启动运行时即可构建分页历史,源归档不被补写 |
71
72 截断文件修复改变了已预留操作的源指纹时,旧操作仍报告生命周期冲突;测试明确要求保留这一证据,
73 不能把旧预留操作强行认证为对修复后字节的成功执行。正常兄弟会话及新完成的导入内容继续可访问。
74
75 ## main-v2 的保留边界与回归组
76
77 | PR 组 | 保护内容 | 验证入口 |
78 | --- | --- | --- |
79 | #9988、#10120、#10241、#10257、#10267、#10326 | Electron 更换、旧事件日志、framed 存储、恢复加速、SessionID | 标签生成器、版本矩阵、原生启动、root Session 测试 |
80 | #10320、#10395、#10440、#10459、#10549、#10566、#10594 | 身份、Windows 路径、按需历史迁移、分支/转换谱系 | 81 个已有迁移/历史测试;DAG 多代转换、源变化隔离、重复启动、历史先行加载 |
81 | #10396、#10409、#10519、#10560、#10623、#10631、#10634 | 归档、恢复、永久删除、最后会话、关闭协调 | workspacestate 全包 race,生命周期回归,输入归档/删除测试,原生最后归档重启 |
82 | #10469、#10551、#10572 | 旧草稿冻结和恢复,欢迎页 | 三套真实草稿库、schema 迁移、恢复状态机、空页/导航测试 |
83 | #10392、#10430、#10545、#10547、#10598、#10626、#10628 | 回执、压缩、附件闭包、导出、inbox、管理命令 | 131 个 root 定向 race 测试;真实旧写入器;Goal/shell/管理命令提交回归 |
84 | #10550、#10590、#10603、#10613、#10619、#10621 | 消息身份、历史分页、只读快照、资源限制 | transcript suite、固定快照/索引回归、标签历史分页 |
85 | 比回滚基线新增的 #10639、#10641、#10642、#10644 | 用户行与输出顺序、Dock 图标、压缩反馈、图形故障恢复 | 最新 main-v2 上三方干净应用回滚补丁;前端双类型检查、生命周期、transcript、输入恢复、38 项顺序/交接断言、Electron 测试和生产构建 |
86
87 main-v2 叠加验证还运行了 283 个 agent/control/transcript 定向测试,以及 13 个 Desktop
88 创建/输入/草稿/停用清理 race 测试。各组有交叉,不能相加作为不重复测试总数。
89 生产构建使用 main-v2 自带的预算,没有为本次改动放宽预算;该叠加构建初始资源为
90 2053.6 KiB / 2053.8 KiB。
91
92 ## 本次测试发现并修复
93
94 1. **未知草稿库被初始化**:版本 0 但已有表的数据库会进入初始化路径;负版本还可能先改 journal mode。
95 现在在任何写入前拒绝未知布局,兼顾另一个进程刚完成首次初始化的情况。
96 2. **1.38.8 导入后分页失败**:原版归档没有 `storage.identity.json`,旧导入仅为改名目标生成 identity。
97 现在每次导入在验证完整事件后、原子发布前补齐目标 identity;已有合法 identity 保持,源数据不改动。
98 3. **收紧降级说明**:真实读取器确认 1.38.10 的会话 revision 边界。保留负向测试,未为迎合旧版降低当前格式。
99
100 原生初测曾发现 rebuildable `display-index.json` 的 `updated_at` 会刷新。这不是会话内容写入;
101 原生源文件不变断言排除可重建 display/event index,仍逐字节核对原 JSONL、事件日志、metadata 和内容对象。
102
103 ## 原生应用证据与限制
104
105 使用隔离的 macOS arm64 **完整打包应用**(Electron 与 Go host 同一构建),没有开发服务器或 service override。
106 逐版本执行正常启动 → 列出旧历史但不隐式导入 → 显式导入 → 读取历史 → 保存正式会话输入 → 正常退出 →
107 重启 → 核对身份/输入/无新草稿/无替代会话 → 核对原源。9 个版本共 39 个样本。
108 另有正式新建、重复操作、图片、正常退出、杀死壳后的异常恢复、归档最后会话测试。
109
110 **未覆盖的边界**:Windows/Linux 的实际打包运行仍需对应系统;没有安装九个完整旧版客户端逐一执行 UI 升级,
111 旧版本证据来自其真实编码器/读取器和当前打包应用。没有穷举 9×9 所有客户端来回组合;数据迁移覆盖逐版直接升级、
112 既有多代格式链、1.38.11 回退再升级及 1.38.10/1.38.9 的拒绝边界。真实 provider 联网执行不在这些合成测试范围内。
113 不能用这些测试声称未来所有 main-v2 PR 或未来格式也兼容。
114
115 ## 复现
116
117 ```sh
118 python3 scripts/generate-session-history-fixtures.py --writers-dir /tmp/reasonix-tagged-writers
119 python3 scripts/generate-manual-session-fixtures.py
120 python3 scripts/session-compatibility-evidence.py
121
122 cd desktop
123 node ../scripts/desktop-windows-go-tests.mjs history-3-5
124 node ../scripts/desktop-windows-go-tests.mjs history-6-7
125 node ../scripts/desktop-windows-go-tests.mjs history-8-9 --race
126 node ../scripts/desktop-windows-go-tests.mjs history-10-11
127 REASONIX_TAGGED_WRITERS=/tmp/reasonix-tagged-writers go test . -run 'TestTagged(Formal|Previous|Draft|Damaged)' -count=1
128 go test -race ./internal/workspacestate ./internal/draftstate ./internal/sessionui ./internal/legacycleanup ./internal/upgradefixture
129 ```
130
131 版本矩阵按不重叠版本分组,避免完整 Desktop 测试包的聚合超时;没有放宽单例断言时间。
132 普通 Desktop CI 使用同一分组逐进程执行,race 与 Windows CI 增加四个历史分组。
133 分组校验从当前平台 Go 测试清单验证每个测试恰好归属一个执行组,保留原生 ConPTY 的单独执行。
134 27 项 CI/分组契约测试通过;没有通过跳过历史矩阵或扩大超时来解决聚合耗时。
135 最终通过同一 CI 入口补跑 `history-8-9 --race`,Desktop 包耗时 176.148 秒。
136 旧写入器往返是显式集成检查,需要先生成对应可执行文件并设置 `REASONIX_TAGGED_WRITERS`;
137 本次已实际执行并通过,普通 CI 不设置该变量时会报告跳过这一项。
138 `tagged-history-smoke.mjs` 接收最终打包可执行文件路径,默认运行所有 9 个版本。
139
140 ## English summary
141
142 The fixtures use actual writers from all nine release tags, plus durable-draft snapshots from the
143 #10469 and #10572 development milestones. Tests cover complete provider history, paged reachability,
144 compaction, branches, interrupted work, opaque fields, SQLite WAL backup, receipts, lifecycle ownership,
145 and stale composer recovery. The current macOS package also imports each release's synthetic histories
146 and survives a normal restart without adding identities or modifying authoritative sources.
147
148 The real 1.38.10 reader rejects storage revision 3 introduced by #10545; registry compatibility does
149 not imply session downgrade compatibility. The real 1.38.11 writer can continue a current-format
150 session; reupgrade preserves the independent input database and requires review of stale input.
151 Unknown draft layouts are now rejected before any write. Imports of pre-recovery archives now publish
152 a target storage identity before history reads. Windows/Linux native execution and full old-client UI
153 roundtrips remain unverified.
154
154 lines MARKDOWN