| 1 | # History loading fails after Stop / 暂停后历史加载失败 |
| 2 | |
| 3 | This document records the original reader repair baseline. The subsequent |
| 4 | append-only termination and revision-2 migration are documented in |
| 5 | [Session termination](session-termination.md); its upgrade rules supersede the |
| 6 | baseline compatibility table below. |
| 7 | |
| 8 | 本文记录最初的读取器修复基线。后续追加式终结和 revision 2 升级见 |
| 9 | [暂停与恢复机制](session-termination.md),升级兼容规则以新文档为准。 |
| 10 | |
| 11 | ## Confirmed mechanism / 已确认机制 |
| 12 | |
| 13 | Stopping a turn can call `Controller.replaceSessionAfterCancel`, which records |
| 14 | `history/replace` with the existing message IDs and `reason` metadata. Opening a |
| 15 | session subsequently reads that event through the history index. Two independent |
| 16 | reader defects made a valid durable transcript unreadable: |
| 17 | |
| 18 | 1. Execution/recovery accepted `reason` and `sourceSequences`, but the history |
| 19 | and search decoders only declared `messages`. Strict decoding rejected the |
| 20 | valid event with `json: unknown field "reason"`. Legacy imports had the same |
| 21 | discrepancy for `source`, `goal`, `modelRef`, and `modelIdentity`. |
| 22 | 2. History replacement retired previous message rows but reset their version |
| 23 | counters. Reusing the same ID inserted version 1 again and failed SQLite's |
| 24 | `(message_id, version)` primary key. Both incremental indexing and rebuilding |
| 25 | from the log failed. Loading only current-row versions also broke restoration |
| 26 | of a message removed by an earlier replacement. |
| 27 | |
| 28 | 暂停回合可能调用 `Controller.replaceSessionAfterCancel`,写入保留原消息 ID、 |
| 29 | 附带 `reason` 的 `history/replace` 事件。随后打开会话会通过历史索引读取此事件。 |
| 30 | 两个独立的读取缺陷使合法记录无法显示: |
| 31 | |
| 32 | 1. 执行和恢复层接受 `reason`、`sourceSequences`,历史和搜索解码器却仅声明 |
| 33 | `messages`,严格解码直接报 `json: unknown field "reason"`。旧会话导入的 |
| 34 | `source`、`goal`、`modelRef`、`modelIdentity` 也存在同类字段不一致。 |
| 35 | 2. 历史替换保留旧数据库行,却把版本计数清零;相同 ID 再写版本 1,触发 |
| 36 | `(message_id, version)` 主键冲突,增量更新和完整重建均失败。另外,只读取 |
| 37 | 当前有效行的版本,也会让先删除、后恢复的消息发生冲突。 |
| 38 | |
| 39 | The fix shares the existing event schemas across execution, recovery, history, |
| 40 | and search. History versions retain their high-water marks across replacement |
| 41 | and are restored from all indexed versions, including retired identities. |
| 42 | Unknown fields and missing/null message lists still fail validation. |
| 43 | |
| 44 | 修复复用已有事件字段定义,使执行、恢复、历史和搜索一致;替换时保留消息版本 |
| 45 | 水位,增量读取时恢复全部身份的最大版本,包括已退出当前历史的消息。 |
| 46 | 未知字段、缺失或为 null 的消息列表仍会被拒绝。 |
| 47 | |
| 48 | ## Evidence and limits / 证据与边界 |
| 49 | |
| 50 | - Base checkout: `b21deef03eb859c2e923f1f6dd8a8ee1b0d4493e`. |
| 51 | - Submitted diagnostics identify preview build `6e4617121fd4`. The real |
| 52 | Stop → switch away → read sequence fails on that exact unmodified production |
| 53 | code with `unknown field "reason"`; the same regression passes with this fix. |
| 54 | - The recorder contains 1,521 events over 120,005 ms, including three |
| 55 | `navigation.settle` / `data-failed` results and one `paint-ready` success. |
| 56 | It contains no backend exception text, so it cannot independently establish |
| 57 | the failing event or session for every reported incident. |
| 58 | - The submitted report's index/log watermark differences are observations, |
| 59 | not proof that every cache must update at each turn end. These indexes are |
| 60 | derived data and can legitimately lag. The confirmed decoder/primary-key |
| 61 | failures explain why a subsequent index read can repeatedly fail to catch up. |
| 62 | - The v5 workspace registry owns session membership; fewer records in legacy |
| 63 | topic tables do not by themselves prove missing conversations. Lease warnings |
| 64 | and concurrent-version damage are not established causes by these artifacts. |
| 65 | - The older base also treats history `preparing` as a failure; the submitted |
| 66 | preview already has preparation waiting. No frontend retry change is included |
| 67 | in this repair of the durable event readers. |
| 68 | |
| 69 | 当前基线为上述 `b21deef…`。在诊断指定的原始预览版 `6e4617121fd4` 上,真实 |
| 70 | “暂停 → 切走 → 读取”回归同样报 `unknown field "reason"`;应用本修复后通过。 |
| 71 | 录制数据有三次 `data-failed`、一次 `paint-ready`,但没有后端异常内容,不能 |
| 72 | 据此断言所有用户每一次失败都只有此原因。报告中的索引落后可作为现象,不能 |
| 73 | 单独证明每回合结束时必须同步所有缓存。旧 topic 数量也不能替代 v5 workspace |
| 74 | 注册表判断会话是否丢失;租约告警、不同版本混用尚无直接因果证据。 |
| 75 | 较老基线的 `preparing` 误报在用户预览版中已有等待处理,本修复未叠加前端重试。 |
| 76 | |
| 77 | ## Compatibility / 兼容性 |
| 78 | |
| 79 | | Surface / 范围 | Behavior / 行为 | |
| 80 | | --- | --- | |
| 81 | | Durable events / 持久事件 | Existing fields and bytes unchanged; readers accept the already valid schema. / 字段与写入字节不变,读取已有合法格式。 | |
| 82 | | SQLite history / 历史缓存 | Same schema and primary key; monotonically increasing versions. Failed index transactions roll back and can be retried by the fixed reader. / 表结构及主键不变,版本递增,失败事务回滚后可由修复版继续读取。 | |
| 83 | | Earlier readers / 旧读取器 | Still contain these defects; a read fix does not repair an old executable. / 旧程序仍有本缺陷,不能靠新版读取修复旧程序。 | |
| 84 | | Provider and RPC / 模型与接口 | No prompt, provider payload, RPC shape, or session identity change. / 不改提示词、模型请求、RPC 结构或会话身份。 | |
| 85 | |
| 86 | All reproductions use disposable sessions. No submitted user data was modified. |
| 87 | Tests cover repeated rewrites, removed/restored identities, search, imported |
| 88 | metadata, cached restart, rebuild after restart, desktop page/window/compatibility |
| 89 | readers, and Stop → switch → return → send. Subsequent Windows ARM64 packaged |
| 90 | execution is recorded in [Session termination](session-termination.md). A retest |
| 91 | of the reporters' original session logs remains external verification. |
| 92 | |
| 93 | 复现全部使用临时会话,未修改用户资料。回归覆盖重复重写、删除后恢复消息、 |
| 94 | 搜索、导入元数据、保留缓存重启、重启后重建索引、Desktop 三种历史读取接口, |
| 95 | 以及暂停后切走、切回、再发送。后续 Windows ARM64 包运行验证见暂停与恢复文档; |
| 96 | 用户原始会话日志复测仍待外部验证。 |
| 97 | |
| 98 | ## Local validation / 本地验证 |
| 99 | |
| 100 | - Passed: session/control regression tests on both the base checkout and the |
| 101 | submitted preview commit with the patch; focused Go race tests; desktop |
| 102 | session switch/cancel/history reader tests; `pnpm test:transcript`; |
| 103 | `pnpm test:transcript-browser` (Chromium); repository lint and diff checks. |
| 104 | - `go test ./...` passed the session, control, and other reported packages except |
| 105 | `internal/cli`: three WSL clipboard fixture subprocesses exceeded their 5-second |
| 106 | deadlines. The exact clipboard suite passed independently on both checkouts. |
| 107 | The aggregate run is therefore not reported as green; no timeout or test was |
| 108 | relaxed for this change. |
| 109 | |
| 110 | 两个代码基线应用补丁后的定向回归、Go 竞态测试、Desktop 会话切换/取消/历史读取、 |
| 111 | 前端 transcript、Chromium 浏览器测试及仓库静态检查均通过。全仓 Go 检查中, |
| 112 | `internal/cli` 的三个 WSL 剪贴板子进程超过 5 秒限制;该组测试在两个基线独立 |
| 113 | 运行均通过,因此保留全仓首次检查未全绿的记录,未放宽超时或测试。 |
| 114 |