| 1 | # Session archive and recovery upgrade / 会话归档与恢复升级 |
| 2 | |
| 3 | ## User behavior / 用户行为 |
| 4 | |
| 5 | Archive remains available. Archived sessions move out of the sidebar into |
| 6 | Trash. Canonical history is retained in place. Restore makes the |
| 7 | original workspace visible and opens the same SessionRef. A missing project |
| 8 | directory does not authorize moving the session into the current project. |
| 9 | |
| 10 | 保留归档能力。归档后会话从侧栏移入“回收站”,canonical 正文原位保留。 |
| 11 | 恢复重新显示原工作区,并打开同一 SessionRef;原项目目录离线不能作为改变归属的理由。 |
| 12 | |
| 13 | Trash provides read-only preview, restore, permanent deletion and empty-trash. |
| 14 | Historical recovery is a separate auxiliary entry, not another lifecycle tab. Historical |
| 15 | entries with ambiguous old `.trash` intent are excluded from bulk purge. |
| 16 | Importing historical data preserves the original files. A successful restore |
| 17 | followed by a list refresh failure is reported as restored with a refresh |
| 18 | failure; retry must not create another session. |
| 19 | |
| 20 | 回收站提供只读预览、恢复、彻底删除及清空。“历史待恢复”是独立辅助入口, |
| 21 | 不设置“已删除”可恢复分页。无法确认旧 `.trash` 操作语义的 |
| 22 | 记录进入历史待恢复,不参与一键清空。恢复历史资料保留旧原件;持久化恢复成功后 |
| 23 | 若列表刷新失败,显示“已恢复,列表刷新失败”,重试不得重复创建会话。 |
| 24 | |
| 25 | ## Authority and compatibility / 权威状态与兼容边界 |
| 26 | |
| 27 | | Owner / 所有者 | Responsibility / 职责 | |
| 28 | | --- | --- | |
| 29 | | Canonical store | Headers, events, messages and content references / 会话头、事件、正文及内容引用 | |
| 30 | | Upstream migration ledger (#10395) | Source/head/conversion receipts and provenance / 来源、head、转换回执与谱系 | |
| 31 | | Registry schema 2 | Workspace membership, lifecycle, provenance and replay operations / 工作区成员、生命周期、来源映射及可重放操作 | |
| 32 | | Indexes and legacy projections | Discovery and presentation only / 仅负责发现及展示 | |
| 33 | |
| 34 | The registry stays at `desktop/workspace-state-v1.json`; its internal version |
| 35 | is 2. Do not create a parallel empty registry. Version 1 is backed up before |
| 36 | the first schema write. Unknown JSON fields survive read/modify/write, |
| 37 | including nested records. Unsupported schemas and states fail closed. |
| 38 | `archivedSessionIds` is a derived compatibility view of `sessionStates`. |
| 39 | |
| 40 | 注册表路径仍是 `desktop/workspace-state-v1.json`,内部版本升级为 2,不能另建空 |
| 41 | 注册表。第一次结构写入前备份 v1 原件;读改写保留未知 JSON 字段及嵌套对象。 |
| 42 | 未知版本、未知状态必须明确失败。`archivedSessionIds` 只从 `sessionStates` 派生。 |
| 43 | |
| 44 | Desktop directory version 5, registry schema 2 and the canonical codec are |
| 45 | independent. This change does not rewrite codec semantics, message IDs, |
| 46 | provider-visible content or event order. Local shell/host protocol is 10; |
| 47 | remote Serve protocol is unchanged. |
| 48 | |
| 49 | Desktop 目录 v5、注册表 schema 2 与正文 codec 分别管理。本次不改变 codec 语义、 |
| 50 | 消息 ID、provider 可见内容和事件顺序。本地 shell/host 协议为 10,远端 Serve 不变。 |
| 51 | |
| 52 | ## Historical sources / 历史来源 |
| 53 | |
| 54 | | Format / 格式 | New reader / 新版读取 | Previous writer / 旧版写入 | Boundary / 边界 | |
| 55 | | --- | --- | --- | --- | |
| 56 | | Registry schema 1 | Convert with immutable backup / 备份后转换 | Retained backup only / 仅独立旧备份 | Forward upgrade / 单向升级 | |
| 57 | | Registry schema 2 | Read and preserve unknown fields / 读取并保留未知字段 | Schema-1 reader rejects writes / schema-1 程序拒绝写入 | No shared editing with old releases / 不支持新旧共享编辑 | |
| 58 | | Future registry schema/state | Explicit error, leave intact / 明确错误并保留 | Unsupported / 不支持 | Never interpret as empty / 不解释为空 | |
| 59 | | Local host protocol 10 | Matching shell/service only / shell 与 service 必须匹配 | Protocol 9 handshake rejected / 拒绝协议 9 握手 | Serve unaffected / 不影响 Serve | |
| 60 | |
| 61 | | Source / 来源 | Handling / 处理 | |
| 62 | | --- | --- | |
| 63 | | Legacy JSONL, checkpoint, schema 1 | Existing import adapter; keep originals / 复用适配器导入,保留原件 | |
| 64 | | Schema 2 DAG | Upstream live-head discovery and proven lineage; keep incomparable histories / 沿用上游存活 head 与已证明谱系,保留不可比较历史 | |
| 65 | | v3, v3.1 and v4 draft | Explicit existing preview adapter into isolated staging; validate before registration / 现有预览适配器隔离导入,校验后登记 | |
| 66 | | Canonical v4 | Validate and copy through canonical export/import / 校验后经 canonical 导出导入 | |
| 67 | | Desktop v5 | Reuse IDs; repair unambiguous missing membership / 复用 ID,补登记可确定归属的成员 | |
| 68 | | Historical `.trash` | Proven recoverable trash becomes archived; ambiguous intent needs recovery review; retain originals / 明确可恢复记录导入为归档,语义不明进入待恢复,保留原件 | |
| 69 | | Unknown/damaged/conflicting sources | Preserve and diagnose; never merge by title or message equality / 保留并诊断,不按标题或正文相同合并 | |
| 70 | |
| 71 | A source key includes filesystem identity and an explicit historical head when |
| 72 | one is selected. The fingerprint covers transcript/event bytes, not mutable |
| 73 | title/index sidecars. Continued destinations are never overwritten by old |
| 74 | snapshots. Changed sources receive distinct recovery versions. Original DAGs |
| 75 | and unmapped auxiliary records remain in the legacy source directories. |
| 76 | |
| 77 | 来源 key 包含文件系统身份和明确选定的历史 head。指纹覆盖正文与事件,不把可变 |
| 78 | 标题、索引 sidecar 当成正文变化。新会话继续写入后不能被旧快照覆盖;来源变化形成 |
| 79 | 独立待恢复版本。原 DAG 和尚无新格式映射的附属记录保留在旧目录中。 |
| 80 | |
| 81 | ## Durability and retry / 持久化与重试 |
| 82 | |
| 83 | Operations progress through `prepared → content_ready → committed`. |
| 84 | Publication reserves target IDs before publishing content. Registry commit |
| 85 | atomically changes membership, lifecycle, source mapping and the completion |
| 86 | receipt. Topic archive commits staged imports as dependencies in the same |
| 87 | registry replacement. A failed target does not partially archive the batch. |
| 88 | |
| 89 | 操作按 `prepared → content_ready → committed` 推进。发布正文前先持久化目标 ID; |
| 90 | 注册表原子提交成员、生命周期、来源映射和完成结果。主题归档将隔离导入作为依赖 |
| 91 | 一并提交,单项失败不能造成部分归档。 |
| 92 | |
| 93 | Replay validates content, workspace and writer ownership again. An external |
| 94 | writer blocks lifecycle replay. Committed restore requests return their |
| 95 | original SessionRef and generation even if the old source is now offline. |
| 96 | Registry corruption stops writes; do not replace it with an empty file. |
| 97 | |
| 98 | 重放重新校验正文、工作区和写入所有权。外部 writer 占用时暂停重放。已提交恢复 |
| 99 | 请求即使旧来源离线,仍返回原 SessionRef 与 generation。注册表损坏时停止写入, |
| 100 | 不得用空文件替换。 |
| 101 | |
| 102 | Runtime rebinding removes obsolete aliases, checks the published SessionRef |
| 103 | on lookup and fences events by generation. Task cancellation verifies both |
| 104 | session identity and the recorder's runtime owner, so an old `task-1` cannot |
| 105 | cancel the replacement session's `task-1`. |
| 106 | |
| 107 | 运行时重绑定删除过期别名,查找时核验已发布 SessionRef,事件按 generation 隔离。 |
| 108 | 停止任务同时校验会话与 recorder 的 runtime owner,避免旧 `task-1` 控制新会话同名任务。 |
| 109 | |
| 110 | ## Backups and rollback / 备份与回滚 |
| 111 | |
| 112 | New permanent-deletion requests atomically validate the archived session |
| 113 | generation and publish `deleted + tombstoned` in one registry write. The |
| 114 | tombstone is the irreversible commit point; later work progresses through |
| 115 | `content_removed → committed`. Before the tombstone commits, restore may win |
| 116 | and the older deletion intent becomes invalid. After it commits, restore is |
| 117 | rejected and cleanup remains resumable. Filesystem ownership and the session |
| 118 | writer lock are acquired before publishing the tombstone; directory movement |
| 119 | and content/cache removal run after the registry callback without holding the |
| 120 | registry lock. Retry validates the deterministic staging receipt. |
| 121 | |
| 122 | Legacy `prepared` purge operations remain readable but are no longer emitted by |
| 123 | new requests. Replay advances one only when its identity, archived lifecycle |
| 124 | and original session generation still match. Restore or rearchive removes a |
| 125 | superseded `prepared` record in the same registry commit; startup replay also |
| 126 | cleans a stale record without closing a runtime or touching files. Invalid |
| 127 | identity or phase combinations are preserved as evidence and fail closed. |
| 128 | Listing Trash is read-only and never triggers purge replay. |
| 129 | |
| 130 | Lifecycle command receipts preserve the original request fingerprint, |
| 131 | operation ID, target set and observed generation. Successes and non-retryable |
| 132 | failures are final per target; only retryable failures run again. A batch |
| 133 | command reaches its terminal `committed` phase when every target is either |
| 134 | successful or finally failed, while `Result.Committed` continues to mean every |
| 135 | target succeeded. A state conflict requires a refreshed, explicit user action; |
| 136 | an old request never adopts a newer generation automatically. |
| 137 | |
| 138 | 新永久删除请求在一次注册表写入中校验归档会话代际,并原子发布 |
| 139 | `deleted + tombstoned`。墓碑是不可撤销的提交点,后续只按 |
| 140 | `content_removed → committed` 完成清理。墓碑提交前,恢复可以先胜出并使旧删除 |
| 141 | 意图失效;墓碑提交后,恢复必须失败,文件清理可以重放。发布墓碑前先取得目录 |
| 142 | ownership 锁和会话 writer 锁;注册表回调返回后再移动目录、删除正文与缓存,且不 |
| 143 | 持有注册表锁。重试继续校验确定性的暂存凭据。 |
| 144 | |
| 145 | 旧版 `prepared` 删除操作仍可读取,但新请求不再写出该阶段。只有操作身份、归档 |
| 146 | 生命周期和原始会话代际仍一致时,重放才会把它推进到墓碑。恢复或重新归档会在同一 |
| 147 | 注册表提交中移除已淘汰的 `prepared`;启动恢复也会在不关闭运行时、不触碰文件的 |
| 148 | 前提下清理过期记录。身份或阶段组合无法解释时保留证据并拒绝自动修复。列出回收站 |
| 149 | 是只读操作,不会触发删除重放。 |
| 150 | |
| 151 | 生命周期命令凭据固定原请求指纹、`operationId`、目标集合和观察到的 generation。 |
| 152 | 成功项与不可重试失败项均为单项目标终态,重放只执行可重试失败项。全部目标成功或 |
| 153 | 已最终失败时,批次命令进入终态 `committed`;`Result.Committed` 仍只表示全部目标 |
| 154 | 成功。状态冲突要求刷新后由用户明确重新操作,旧请求不能自动采用新代际。 |
| 155 | |
| 156 | Interrupted deletion remains visible in Trash. Shared objects, upgrade |
| 157 | originals and independent backups are retained; this is not secure erasure. |
| 158 | Empty-trash freezes the confirmed target set and reports partial failure. |
| 159 | |
| 160 | 删除中断的条目仍显示在回收站。共享对象、升级原件和独立备份保留,不承诺安全 |
| 161 | 擦除。清空回收站冻结确认时的目标集合并反馈部分失败。 |
| 162 | |
| 163 | Metadata backups are immutable and content-addressed. They include the |
| 164 | registry, project/tab state, migration ledger, legacy topic JSON and consistent |
| 165 | SQLite topic snapshots including committed WAL pages and unknown tables. |
| 166 | Backup failure prevents the schema upgrade. Large history originals remain |
| 167 | in place rather than being destructively moved. |
| 168 | |
| 169 | 元数据备份按内容寻址且不可覆盖,包括注册表、项目/标签状态、迁移台账、旧主题 |
| 170 | JSON 和包含已提交 WAL 及未知表的 SQLite 一致性快照。备份失败不得升级结构; |
| 171 | 大文件正文原位保留,不做破坏性迁移。 |
| 172 | |
| 173 | Only newer readers/writers may use schema 2. To run an older application, |
| 174 | restore pre-upgrade metadata into an **independent copy** of the old data. |
| 175 | Never overwrite the current registry with a pre-upgrade backup after new |
| 176 | sessions have been created. A production rollback needs a build that still |
| 177 | understands schema 2. This work does not authorize a public release. |
| 178 | |
| 179 | 只有支持 schema 2 的程序能使用升级数据。运行旧程序时,在**独立数据副本**中 |
| 180 | 恢复升级前资料。产生新会话后,不能把旧注册表覆盖到当前目录。线上回退应使用 |
| 181 | 仍理解 schema 2 的修复构建。本次实施不包含正式发布。 |
| 182 | |
| 183 | ## Qualification / 验收 |
| 184 | |
| 185 | Release requires archive → restart → restore → sidebar → readable history → |
| 186 | restart, failure/replay tests, identity and task isolation, separate root and |
| 187 | Desktop Go suites, race checks, frontend tests/typecheck/build and a real |
| 188 | production Electron package with protocol handshake and clean exit. Native |
| 189 | Windows/Linux runs must be reported separately from cross-compilation. |
| 190 | |
| 191 | 发布门禁包括归档→重启→恢复→侧栏可见→正文可读→再次重启、失败重放、身份与任务 |
| 192 | 隔离、根模块与 Desktop 独立测试、竞态检查、前端测试/类型检查/构建,以及真实 |
| 193 | Electron 生产包握手与干净退出。Windows/Linux 原生运行与交叉编译必须分别报告。 |
| 194 | |
| 195 | ## Purge admission and interrupted staging / 删除准入与暂存中断 |
| 196 | |
| 197 | Purge command admission accepts an older global snapshot while rejecting future |
| 198 | generations. Each target's generation is checked in the tombstone transaction; |
| 199 | unrelated session changes do not veto deletion. Archive, restore and historical |
| 200 | import admission remain unchanged. Request receipts retain the original snapshot. |
| 201 | |
| 202 | Normal deletion resumes a valid legacy `prepared` operation by carrying its observed |
| 203 | identity and the request generation into the registry lock. Removed or replaced |
| 204 | operations conflict; recovery never creates replacement deletion intent. A crash |
| 205 | after staging-receipt creation but before rename resumes using the validated receipt, |
| 206 | including when the filesystem wraps its already-exists error. |
| 207 | |
| 208 | 删除命令允许较旧的全局快照,拒绝未来版本;最终墓碑事务校验目标会话版本,其他会话 |
| 209 | 变化不会否决删除。归档、恢复及历史导入的准入保持原规则,凭据保留请求原始版本。 |
| 210 | 普通删除携带旧准备记录的观察身份和原请求版本,在注册表锁内重新校验并继续;记录 |
| 211 | 消失或被替换时冲突,不创建替代删除意图。暂存凭据创建后、重命名前崩溃时,重启 |
| 212 | 复用已校验凭据,正确识别被包装的文件已存在错误。 |
| 213 | |
| 214 | Deterministic child-process tests cover filesystem and registry crash boundaries. |
| 215 | A frozen previous-v2 implementation checks old reads and unrelated-field writeback. |
| 216 | Format compatibility does not fix old writers' races; upgrade all shared-directory writers. |
| 217 | |
| 218 | 确定性子进程测试覆盖文件及注册表中断边界;固定上一版 v2 实现验证旧读取与无关字段 |
| 219 | 写回。格式兼容不表示旧写入器的竞态已修复,共享目录进程应统一升级。 |
| 220 |