文件修改、Review 与历史恢复
本页统一定义文件归因、单轮 Undo、历史 Revert、多轮 Rewind、对话 Branch、Review scope、冲突处理和恢复存储。聊天视觉规范只负责外观;任何实现、测试和宣传口径均以本页语义为准。
在同一个窗口查看、编辑和找回文件版本
打开文件先预览,进入编辑后自动写入。History 显示人工与模型修改,恢复前检查当前版本。下方是交互设计演示,格式引擎与保存流程仍在实现验收中。
最近修改先保存在本地草稿,后台写入文件。回到标签时继续编辑;不需要 Save,也不常驻显示 Saved。
显示“文件已被其他操作修改”。保留当前草稿,可查看磁盘版本或另存副本;不自动覆盖。
使用与当前文件相同的预览器。恢复是一次新的有记录操作,保留恢复前内容,支持重新应用。
常见格式的实际目标与复用方案
文本、代码、Markdown:预览、源码编辑和自动保存。DOCX、PPTX、XLSX:本地完整引擎预览与编辑,必须通过真实文件的打开、修改、导出和重新打开验证;不能以提取文字替代页面或幻灯片预览。旧 DOC/PPT/XLS 与 ODT/ODS/ODP 纳入兼容测试,若引擎只能转换则明确另存新格式,不覆盖原格式。CSV/TSV 至少可表格预览和文本编辑。PDF:分页、缩放和搜索,批注或编辑按组件实际能力开放。PNG/JPEG/WebP/BMP/GIF/SVG/TIFF:预览;静态位图提供裁剪、旋转与标注,动画或矢量不得在普通保存时静默栅格化。PSD:合成图和图层信息预览,保留原始 PSD;只有通过无损回写验证的操作才能启用原格式编辑。音频与视频复用本地 HTML 播放器。无法解析、加密、超限或尚不支持的文件有明确状态与下载入口。
候选 Office 方案包括 ONLYOFFICE Docs、OnlyOffice browser/WASM 与 LibreOffice WASM。默认方案必须本地可部署,不要求用户上传文档到第三方网站。完整服务器方案保留为可配置适配器候选,不能作为默认安装缺失时唯一能力。
PDF.js 是 PDF 浏览候选;TOAST UI Image Editor 是位图编辑候选,关闭 usageStatistics 并自带资源;ag-psd 是 PSD 预览候选,其色彩模式、文字图层与效果限制要求保留原文件。Photopea 支持嵌入但属于第三方托管页面,不默认加载或传入本地文件。最终选型以固定版本源码与本地样例验证为准。
统一打开、编辑和版本协议
文件引用包含稳定的项目或会话归属及相对路径,显示路径不是权限凭证。能力识别先核实扩展名、类型和文件内容,再选择预览或编辑适配器。适配器只获得该文档的内容与版本,不获得任意磁盘权限;统一导出 bytes/Blob、修改事件、错误事件和销毁方法。Office 与图片大资源只在打开相关文件时加载,不能拖慢聊天首屏。
人工编辑与模型写入共享版本校验、完整文件发布和 immutable before/after 存储原则。既有 project_file_write 的幂等记录只有操作结果与指纹,不等于可恢复的文件版本;本次补齐实际版本保存。人工修改记录 actor=user,模型修改关联 session/turn,不伪造聊天轮次,也不把人工历史变成侧栏会话。History 按文件汇集来源,模型本轮 Review 继续保留其归属语义。
版本数据按归属保留:项目中的人工编辑历史归项目,模型记录仍关联实际会话与轮次;汇总索引只引用已有版本,不另复制来源文档。归档会话仍可打开原记录;彻底删除会话后,其入口与归属版本按现有删除规则清理,汇总页不得用缓存重新显示原文。Memory 中残留来源仅标记“原会话已删除”或移除链接,不为此建立来源副本。项目文件及其独立人工历史不因删除一段聊天而一并删除。
自动保存和版本归档分开:编辑器正常撤回处理即时修改;草稿先持久保存,文件在有修改事件后的短暂合并窗口自动发布;History 默认按连续编辑时段归档,间隔约五分钟形成版本,切换文档、结束编辑或恢复操作也完成归档。文件内容不等待五分钟才保存。每次发布校验读取时的 revision,重复请求幂等,乱序响应不能覆盖新草稿;保存失败保留草稿并显示可重试错误。重启后读取持久记录,不根据当前文件补造过去版本。
富文档编辑器的生命周期独立于标签视图。切换标签时先隐藏视图,仍在导出的引擎继续保留;最新内容进入持久草稿后才能释放引擎。Office iframe 在同一文档会话中不因 React 重新挂载或 DOM 移动而重建。关闭、重命名和版本恢复均先等待最终导出,失败保留编辑器和草稿。预览与编辑复用同一实例;只读历史实例不能回写当前文件。
论文阅读与批注
上游查看器样式表及其根变量都限定在 PDF 阅读器范围内。例如以下通用名称: .sidebar 不得改变外层应用界面。
PDF 工作区遵循提供的 PDF Expert 参考中的紧凑阅读布局:现有 OpenProgram 文档标签页位于扁平工具栏上方,可折叠左侧栏包含缩略图与文档大纲,其余空间连续显示 PDF 页面。阅读控件提供页面导航、适应宽度/页面与数值缩放、文本选择、文档内链接和搜索结果高亮。只显示已实现的阅读与批注工具;原文编辑、OCR、页面结构操作与格式转换不在本轮范围内。
视觉系统继承 OpenProgram 字体与浅色/深色主题变量:中性工具栏、白纸、深色阅读背景、低对比分隔线与蓝色选择强调。参考配色:正文 #202124、工具栏 #27272a、画布 #151517、纸张 #ffffff、分隔线 #414145、焦点 #4a90e2。工具栏标签使用13px应用字体,控件高32px。桌面端使用单一阅读工具栏与上下文批注行;窄面板中控件换行,不覆盖文档文字。侧栏宽180px且可隐藏。使用真实渲染页面,不提供占位内容或装饰性仪表盘元素。
使用已固定的 PDF.js 6.3.289 legacy 查看器库、文本层、链接服务、搜索控制器与批注编辑器,保留本地模块/worker 兼容性与生产 CSP。上游 查看器源码 定义生命周期与编辑器 API。PDF Expert 的 Mac 功能参考 提供阅读与批注对照。备选的 EmbedPDF 查看器 本轮不采用:所需阅读、高亮与文字批注能力不需要替换解码器或引入第二个渲染引擎。
复制 PDF 文本层选区时自动合并排版换行。保留段落换行与原始标点,连接 CJK 文本时不插入空格,保留普通连字符并移除可选软连字符。这仅是本地剪贴板转换,不编辑 PDF,也不作用于搜索框、批注文字编辑器或其他应用内容。可见复选框允许保留原始换行。
项目 PDF 通过 PDF.js 支持高亮与自由文本批注,使用现有 DocumentController 富编辑器租约、持久草稿存储与乐观版本检查保存标准 PDF 批注。防抖导出、显式保存、关闭、历史恢复与重命名沿用 Office/位图文档的控制器归属。更新草稿字节不得重建活跃查看器或丢失撤销栈。历史快照与附件保持只读。不启用 PDF 自带脚本或外部文档上传;保留原始字节、输入与画布限制、错误处理和下载。
验收通过真实 FileViewer/DocumentWindow 入口,在生产 CSP 与桌面兼容 API 下执行。验证连续非空页面与可选文本,涵盖普通文本/CJK/段落的剪贴板内容,页面/侧栏/大纲/缩放/搜索行为,只读历史/附件,高亮与批注创建,真实 PDF 字节的保存及关闭/重开,通过独立解码器验证持久批注,失败保留和禁止过期编辑器发布。仍须执行现有文件/历史/控制器、完整 Web、仓库契约、浏览器与文档检查,之后进行两项独立审查与已安装 App 检查。
PDF 运行时兼容性
本地 PDF 解码器固定为 PDF.js 6.3.289。主模块与 worker 使用匹配的 legacy/build 发行构建,通过现有同源 /document-assets/pdfjs/ URL 提供。上游 浏览器支持指南 与包 README 要求缺少较新 JavaScript API 的环境采用此构建。现代构建要求 Uint8Array.prototype.toHex,桌面 Electron 37 / Chromium 138 运行时缺少该 API。Legacy 配对在两个执行上下文都提供上游兼容代码;仅主页面的 polyfill 无法修复 worker。
保留已审查解码器版本、本地 CMaps、字体、WASM、许可证清单、延迟加载、64 MiB 输入上限、画布限制、禁用脚本和原文件下载。本次修复不升级 Electron,也不添加应用自有全局 polyfill。生成的 manifest 同时标明版本与构建类型。只读 PDF 能力保持不变。
应用界面允许 connect-src 'self' blob: ,使解码器可通过对象 URL 读取保留的本地文件版本。这只扩展本地字节读取;外部 HTTP/WebSocket 目标仍被阻止,不启用 blob 脚本、eval、对象或第三方框架。PDF 验收将真实应用策略用于测试页,包含外部请求被拒绝的探测。宽松独立浏览器测试不能证明已安装 App 行为。
验收使用公开 FileViewer 入口验证非空页面渲染、导航、缩放、文本搜索、无效输入恢复与无运行时错误的关闭/重开。在主页面与 worker 缺少原生 toHex 和 Math.sumPrecise 时执行相同检查,并在已安装桌面端使用报告中的 PDF 检查。资产 manifest 必须标识实际暂存模块与 worker 字节。这些检查补充完整 Web、仓库契约、浏览器与文档检查。
可选 Office 安装
Office 文档支持是演示文稿、文字处理文档与电子表格共用的可选本地组件。默认 App、runtime 与 Python 构建不包含其资产,也不下载。打开尚不支持的 Office 文档时只检查本地可用性;缺失时文档界面说明支持格式、约699 MiB下载、本地处理和 ONLYOFFICE 许可证,并提供“安装”与“取消”。取消只关闭安装选择,不下载,之后可再次选择。PDF 查看保持独立。
只有经 owner 认证的本地来源 POST 可从固定发布 URL 与固定归档 SHA-256 启动安装。安装器限制下载与解压,拒绝不安全归档路径和链接,验证完整 Office manifest,并将资源包原子发布到 profile 缓存。并发请求共享一次安装。失败保留此前选定资源包并删除临时下载。安装完成后,当前 worker 无需重启即可提供父模块与隔离宿主,文档自动打开。关闭文档会停止其 UI 请求;已明确请求的安装可继续完成以供其他文档使用。
此前安装的 profile 资源仍可使用,旧发布随 App 打包的资源不再被选用。开发暂存仍是编辑器测试的显式独立命令;普通构建忽略过期暂存包。不引入通用扩展注册表、自动更新、卸载控件或第三方文档上传。
Office 加载
Office 启动时,在居中且适配主题的加载浮层下保留完整尺寸渲染区域。状态遵循应用语言,区分资源加载与文档打开,不使用模拟百分比。启动错误替换状态并提供明确重试入口。
适配器使用固定上游 mount/activate API,关闭正在加载的文档会立即销毁待处理宿主。AbortSignal 取消可用性请求与待处理激活;有界启动期限从激活前开始。失败或取消的实例不附加到文档控制器。预览/编辑切换保留已有编辑器与原生撤销。独立 Office origin、资产身份检查与持久化回调保持不变。这减少不必要的并发启动工作;原生冷转换耗时与加载展示分别测量。
Office 在独立来源加载本地固定版本资源,只开放该资源树的 GET/HEAD,不开放主应用 API、认证和 WebSocket。父子页面同时校验来源、实例和消息请求,宏默认禁用。大型资源由用户确认后单独安装到本地缓存;打开文件仅检查安装状态,不自动下载;远程部署没有配置独立资源来源时明确显示不可编辑,不转交第三方网站。PDF、图片与媒体按需加载,保持聊天首屏资源预算。文件窗口只在打开文件时加载;KaTeX 保留完整 WOFF2 字体,不再重复引用面向旧浏览器的 WOFF/TTF 回退。资源检查保留 2 MiB 首屏压缩传输与 350 KiB 单块压缩脚本上限。
大型二进制通过受限字节上传或本地暂存传输,不编码到普通聊天消息。边读边检查体积,取消时清理临时文件;引擎消息校验来源、实例和请求 ID。文档脚本、宏、HTML/SVG 活动内容不获得主窗口权限;第三方服务仅在用户主动配置并知悉文件将送往该服务时使用。原格式不支持安全写回时,提供明确的导出副本,而不是把 PNG 或 OOXML 内容写进其他扩展名。
完整交付与验收,不以部分格式通过替代整体完成
工作顺序:验证复用引擎;完成统一文件版本与二进制发布接口;接入格式预览和编辑;统一预览优先、自动保存和 History;验证历史恢复、窗口切换、重启与错误状态;执行独立规范与质量审查及组合版本全量检查;最后更新默认 App。每个适配器都需要真实有效文件,不用伪造二进制证明格式支持。
验收逐项覆盖 Word、PPT、表格、图片、PSD、PDF、文本与媒体。可编辑格式须实际修改内容、写回、重新打开并验证版本恢复;只读格式须实际渲染并明确能力。包含并发外部修改、写入中断、损坏文件、超大文件、取消、断网、项目路径变化和旧历史兼容。所有可见支持状态、源代码、测试与安装版本一致后才标记完成。
一个模型,四种操作
四个词不能互换。操作名称直接表达修改代码还是对话、作用于一轮还是一段历史。点击左侧操作查看完整影响。
文件版本的完整生命周期
同一个历史入口处理文本、Word、PPT 和后续文档工具。完整文件负责恢复;文本或格式预览只是显示方式,不改变原始版本。以下展示使用状态,内部实现展开后查看。
Proposal.docx
第一轮:原文 → 修改后
同轮继续修改:更新最终版本
历史仍保留原文;新版本提交成功才替换记录
Proposal.docx
文件写入结果尚未确认
重新打开可看到未完成状态,不显示为“没有修改”或允许不确定恢复
无法读取文件修改记录
保留原记录并报告错误,不重建空历史,不继续覆盖目标文件
正常保存、失败重试与旧版本保护
每份 before/after 使用独立且完整写入的版本文件。持久化顺序是版本内容、校验、版本目录,再发布 manifest。更新 manifest 失败时,前一份成功记录引用的内容仍完整。失败尝试不覆盖已发布版本。只有尚未成功的尝试被明确取消后,重试才以重新读取的当前文件建立 before;已成功修改始终保留本轮最初 before。
准备记录与提交记录分开。未完成的写入保留待确认标记,历史投影显示不可精确恢复,不能推断脚本成功,也不能用当前磁盘内容补造 after。取消且确认没有成功提交的尝试不展示为已修改。损坏或不支持的 manifest 返回明确错误;仅真正缺失的 manifest 表示没有记录。旧版本 manifest 和旧 blob_ref 继续可读,无批量重写迁移。
DeepSeek 与 Codex 的源码依据
DeepSeek 的 JSONL 持久化 使用同步临时文件、独占发布与目录同步,并明确区分损坏记录和缺失记录;会话修复 把中断工具标为结果未知,而不自动补造成功。采用这两项原则保护版本内容与中断状态。其 fs-local 提供目标锁、版本条件检查和文本写入结果,但未提供完整文件 undo/reapply 或 Office 版本管理;因此保留 OpenProgram 现有恢复事务,不把会话日志恢复等同于文件恢复。Codex 的 exact delta 归因继续保留,不把任意 Bash 操作视为精确修改。
新工具如何接入,哪些能力不属于本次实现
本地生成器统一调用 write(file_path, source_path=...) 发布完整文件。文本工具保留 write/edit/apply_patch 入口,共享 checkpoint 的 prepare/commit/abort 合同。不能只提交模型声称的“修改成功”,也不能执行任意 Bash 后扫描目录并归因。状态存储由 CheckpointStore 管理,Review 和会话摘要只作投影,不另建版本副本。未来 Office 语义差异只读取不可变版本,不承担撤销;远端存储需要条件写入和可验证版本标识,在具备这一能力前不宣称支持精确恢复。
本次保留现有单轮聚合、会话分支和恢复事务;不新增全盘扫描、定时轮询、通用插件注册框架、Office 修订视图或远端存储实现。已丢失的历史不能补回。系统崩溃与不遵守文件锁的外部程序仍可能造成待人工处理状态;系统保留证据并拒绝不确定覆盖,不承诺所有外部修改都能自动恢复。
实现与验收计划
先复现连续写入提交失败、失败后重试和损坏记录覆盖;再修复共享历史存储及未完成记录投影。通过注册工具、重新打开会话、Review 与恢复入口验证,最后进行独立规范审查、质量审查和全量检查。该节对应共享历史存储、会话摘要与 Review 投影已实现;独立规范和质量审查、故障回归及全量测试均通过。
文档修改与版本恢复
Word、PPT、PDF 与图片使用完整文件版本。打开这一轮的文件记录可确认修改的文档;撤销恢复修改前的文件,重新应用恢复修改后的文件。重启后仍读取同一份持久记录。
Proposal.docx · 文件内容已更新
查看文件记录 · 撤销本轮修改
同一轮仍显示 Proposal.docx,历史版本不依赖脚本输出或内存缓存。
查看记录 · 重新应用
撤销前检查当前文件。内容与已保存版本不一致时阻止恢复,保留后续修改。
文件已变化,未执行恢复
实现方式与范围
脚本将结果写到独立临时文件,再通过现有 write(file_path, source_path=...) 发布。content 与 source_path 必须且只能指定一个。source_path 是本地普通文件,受读取权限限制;目标继续执行写权限、读取基线与审批文件指纹检查。来源和目标均纳入审批等待时的文件指纹;发布前复核。工具在目标目录生成临时文件,完整写入后替换目标,失败清理临时文件,并保留原目标。工具按原始字节写入,记录 before/after,不将文档转成文本保存。源与目标相同、目录、链接、超限或不可读取的来源在修改目标前失败。来源读取有 64 MiB 上限。已有目标在 advisory lock 下复核打开的文件句柄、内容和路径;这只能串行化遵守该锁的写入,不能阻止外部程序在最终校验与替换之间写入。Windows 在替换前关闭不共享删除权限的句柄并再次校验,因此不承诺操作系统级条件替换。
参考 Codex TurnDiffTracker 的 exact delta 检查与 shell 快照 的暂存、验证、替换流程;后者保存 shell 环境,不是文档版本。沿用 Codex 的明确修改操作归因:通用 Bash、后台进程与外部编辑器不自动成为本轮精确修改。脚本不能直接覆盖最终目标后再补写历史。保留既有文本 write 参数、失败语义、回退事务、分支归属及持久会话布局。文档格式、图片和排版均由完整字节版本恢复;当前不提供 Word 修订标记或 PPT 视觉差异。
读取基线必须先建立:模型先用 read 读取原文件,再生成暂存版本。二进制读取返回文件类型与字节数,不向模型输出乱码。新文件不要求先读。脚本工具说明明确这一流程,无额外保存按钮,无全盘扫描与轮询。
验收经注册 write 工具、会话重新打开及公开历史操作完成:创建和覆盖 DOCX/PPTX 后保存版本;撤销及重新应用逐字节一致;外部更改导致冲突;失败不替换目标;旧文本写入仍有效。
当前实现与必须修复的缺口
以下均由当前源码确认。它们解释了十几万行误统计、响应慢和多轮恢复可能产生部分成功的原因。
| 当前路径 | 现状 | 风险 | 目标 |
|---|---|---|---|
agent_loop._snapshot_cwd | Bash 前后 cwd 扫描、候选预复制和执行器调用已删除。 | 普通 Bash、后台日志和外部写入不再进入 turn mutation。 | 完成 仅 trusted file mutator 生成 receipt。 |
_checkpoint_changed_files | 该路径已删除;prepared receipt 明确区分 regular before image 与 absent。 | 超过统计预算的既有文件保持 modify,stats 为 unknown,不再按新文件整量新增。 | 完成 baseline 缺失不编码成 absent。 |
CheckpointStore.restore_turn | 单轮入口使用全量 plan、profile-stable lock、durable intent、条件式 leaf mutation、verify 与 rollback;legacy 方法只保留给迁移测试,生产 revert_turn 不再调用。 | 冲突、缺 blob、stale plan 均零写入;外部 pathname/open-fd race 保留外部内容并进入 recovery_required。 | 完成 单轮与多轮均使用事务式 apply。 |
rewind_to | 使用 active suffix 一次规划、同路径折叠、dirfd/no-follow 条件写、durable intent、文件验证后 HEAD CAS;不逐轮 revert 或改写旧节点。 | 文件、plan、父目录、HEAD 或 branch version 变化均阻止提交;mixed crash state 在启动时安全回滚或进入 recovery_required。 | 完成 WS 默认只返回 plan,apply 必须携带 plan hash 与幂等键。 |
rewind_to + 调用方子树 | 同 session、非 worktree、带显式 origin 的 owned child 递归纳入 causal closure;每个 receipt 带持久单调 mutation_sequence,同路径按实际 workspace 顺序折叠。 | running owned 或同 workspace linked actor 阻止恢复;completed/cancelled/errored 且有 durable head 的 owned change set 进入同一事务,legacy ambiguous job 不获得恢复权限。 | 完成 linked peer/cross-session 与 worktree 只报告影响,不并入父 change set。 |
/api/chat/checkout | 以一次 HEAD CAS 同步写入 workspace mismatch;连续 checkout 保留真正物化文件所属的 source head。 | 下一次 mutating turn 在明确选择 Keep current files 或 Restore branch code 前被阻止;两个决定均绑定 source/target,stale 请求不能采用新 checkout。 | 完成 Restore 复用事务式 preflight、幂等键、rollback 与 crash recovery,且不移动 conversation HEAD。 |
ProjectGit.revert_agent_commit | 已从单轮 Undo/Revert 生产路径移除 ProjectGit 调用。 | 单轮历史恢复不再 reset/revert 用户 Git,不读取或修改 index/commit。 | 完成 Workspace Git 操作保留独立入口。 |
ShadowGitStore | Turn/Branch Review 与 diff 只读 exact mutation journal;Workspace Review 只读 Git status/numstat。Shadow Git 不再是 Review 事实源。 | legacy shadow commit 路径仍存在,但不再影响文件卡、Review 或恢复资格。 | Review 降级完成 后续可独立清理 legacy cache。 |
| 检查点回收 / 未知轮次 | plan 必须证明目标 turn、全部 actions 与 recovery blob 可用;unknown、expired、corrupt 或不精确 receipt 不显示可执行历史 action。 | Review metadata 仍可读,但 code restore 返回 unavailable;不会写 Reverted metadata或移动 HEAD。 | 完成 Expired 只允许只读 Review。 |
| 分支元数据 | 统一 restore transaction、HEAD/version CAS、branch refs、writer epoch 与 checkout workspace alignment;source leaf 保留,旧 conversation 节点不因 Rewind 被全局隐藏。 | checkout、Keep、Restore 都绑定 expected source/target;并发 HEAD 或文件变化返回 blocked、rolled_back 或 recovery_required。 | 完成 branch projection 与 Review 使用同一 mutation sequence。 |
TurnFilesChips | 聚合值与前三个文件随 assistant metadata 持久化;legacy 仅在进入视口后查询。卡片最多挂载 20 行,不再挂载 diff。 | Review 文件列表每页 100,diff 每页最多 200 行 / 256 KiB,并用 request id + snapshot 防止旧响应和跨 scope 读取。 | 完成 点击文件或 Review 打开确定性的 center Review tab。 |
| Web / TUI / Python CLI | Web WS 与 Python CLI 统一使用只读 plan、显式 confirm/apply、plan hash、幂等键和结果状态;Web center Review 已接入三个 scope。 | Web diff/payload 有界且 actor/producer/linked impact 可见;TUI/CLI 保持同一恢复协议,不复制 Web center pane。 | 协议与 Web Review 完成 |
| 项目文件浏览器 | 目录按展开时加载;搜索只在已加载目录的节点中进行。 | 未展开目录中的文件不会被搜索到;目录结果没有可复用的稳定 snapshot/cursor 合同。 | 待实现 project-scoped 后端搜索、目录分页与稳定 cursor。 |
| 文件 WebSocket 生命周期 | 部分 Review 请求已回显 request_id;项目文件请求仍依赖按响应 type 的全局串行队列,list_turn_files 没有 request id。 | 并发时不能由每个调用独立认领响应;全局串行会把无关的文件加载相互阻塞。 | 待实现 所有文件相关 WS action 强制 request_id 并按该 id 关联响应。 |
| 读取缓存与草稿 | 读取缓存和未保存草稿均无容量上限;草稿只在页面内存中保存。 | 长期使用会累积内存;刷新页面可能丢失 dirty draft。 | 待实现 有界 LRU read cache 与独立、持久化的 dirty draft store。 |
| 审查筛选与分页 | Review 先取一页文件,再在客户端按 Code/Tests/Docs/Large 分类。 | 分类结果只代表当前页;不同页和 workspace 变化之间不能形成稳定的筛选视图。 | 待实现 server 先筛选与排序,再对固定 snapshot 分页。 |
官方参考、采用与边界
| 系统 | 确认行为 | 采用 | 不采用或未知 |
|---|---|---|---|
| Claude Code 检查点 | 每个 user prompt 建 checkpoint;只跟踪 Write/Edit/NotebookEdit,明确不跟踪 Bash。可分别恢复代码、对话或两者;无 tracked edit 时不显示代码恢复。除前台 context: fork skill 外,其他 subagent 修改不恢复;symlink/hardlink/non-regular path 会跳过并警告,因此可能部分恢复。每 session 最近 100 个文件 checkpoint;checkpoint 随 session 保存并受默认 30 天、可配置 cleanupPeriodDays 清理。 | 采用 prompt checkpoint、分离恢复选项、明确 unavailable。 | 调整 OpenProgram 对任一路径失败执行整体阻止或事务回滚,不接受 skipped 后整体成功。超过 100 后旧 prompt 是否仍支持 conversation-only rewind 未获官方保证。 |
| Claude Code 分支 | /branch 与 SDK fork 把 conversation 复制到新 session,原 session 保留;明确不复制或回退 filesystem。VS Code 另行提供 Fork conversation and rewind code。 | 调整 OpenProgram 保留同-task Branch,并新增 source-preserving new-task Fork;with-files 在 Git 项目用隔离 worktree。 | 不采用 fork 后默认复用当前 files 却不说明。 |
| Claude Desktop | session diff 入口;左文件列表、右单文件 diff;支持行级评论和 Review code。 | 采用 独立 Review、单文件按需 diff、结构化行级反馈。 | 不复制 品牌、图标、尺寸、工具栏顺序和 PR 控件。 |
| Claude Web | 确认 session diff、左右布局、行评论和 Create PR;官方页面没有确认 Review code。 | 采用 Review 与聊天分离。 | 未知 不把 Desktop 的 Review code 能力推定到 Web。 |
| Codex 精确轮次差异 | TurnDiffTracker 只在当前 turn 内消费 exact AppliedPatchDelta,不重读 workspace;不精确时使 turn diff 失效。它是内存聚合器,不是持久恢复日志。 | 调整 采用 exact delta 作为归因输入;OpenProgram 自行增加持久 receipt、digest、before/after blob 和 transaction 状态。 | 不声称 Codex 提供 durable Undo journal。 |
| Codex 历史 | thread/fork 创建新 thread ID、只复制 history;TUI 编辑旧 prompt 使用 source-preserving fork。旧 thread/rollback 已废弃,实验性 thread/revert 只处理 conversation;两者都不恢复文件。 | 采用 source-preserving fork 和不可变历史。 | 未知 Codex App 是否另有未开源的 per-turn checkpoint/turn-card file Undo。 |
| Codex Git 审查 | Repository Review 区分 Last turn 与 Git 状态,并提供 Unstaged、Staged、Commit、Branch、多 repo、行评论和 Git whole/file/hunk stage/revert;TUI /diff 对 untracked 使用 --exclude-standard。 | 采用 Workspace Git 子范围和 ignored 排除。 | 分开命名 Git Discard 不是 checkpoint Restore;具体 desktop Git revert 实现不当成 turn Undo。 |
目标架构与信任边界
对话 DAG、精确 mutation journal 和 Git workspace view 是三个独立事实源。历史操作 planner 可以读取三者,但只有 journal 中的可恢复 receipt 能授权文件恢复。
文件浏览、搜索与 Review 查询合同
Project file explorer 与 Review 都使用 project/session 绑定的服务端快照。目录、搜索和 Review 过滤先确定完整的有序候选集,再返回一页;客户端不得以当前页数据推断项目或 scope 的完整结果。
| 查询 | 事实源与范围 | 排序、筛选与分页 | 无效化 |
|---|---|---|---|
| 目录列表 | 由 project_id 解析的项目根目录;path 必须是规范化的根内相对目录。排除路径穿越、符号链接逃逸和服务端禁止浏览的目录。 | 先按类型(目录、文件)和根内规范化路径稳定排序,再建立 snapshot。每页最多 100 项;cursor 是不透明 token,不是数组下标。 | 目录版本、ignore 规则或请求中的 project/path/sort 不匹配时返回 STALE_SNAPSHOT,客户端从第一页重取。 |
| 项目搜索 | 服务端遍历同一项目根,并应用与目录浏览相同的访问与忽略规则;不依赖树已展开的节点。结果只包含名称或根内相对路径匹配的项目文件与目录。 | 先以 query、匹配模式和类型过滤,再按稳定 rank、根内路径排序并建立 snapshot;分页只作用于已过滤结果。搜索 query 或模式变化必须丢弃 cursor。 | 项目切换、搜索 snapshot 到期或根目录版本变化后显示可刷新状态;不得把其他项目的旧结果保留在新项目中。 |
| 审查范围 | This turn / Current branch 读取 mutation journal;Workspace 读取 Git 的 HEAD、index 和 worktree。每一项继续携带 source、actor 和 change-set 归因。 | 服务端先按 category、path query、operation、diff state 与排序过滤,随后对该过滤结果建立 snapshot 并分页。计数和 totals 针对完整过滤结果,不是当前页。 | scope、assistant message、filter、sort 或 snapshot 任一不匹配时拒绝 cursor;Workspace 还比较固定 candidate list 与每个候选的 index/worktree identity。选择的文件若不在新 snapshot 中,清空 selection 和 diff。 |
| 审查文件差异 | 只接受已由同一 Review snapshot 返回的 path;turn/branch 读取 journal blob,workspace 读取 snapshot 中记录的 HEAD/index/worktree blob pair。 | diff cursor 同样是不透明且绑定 file identity、blob pair、scope snapshot 与行预算的 token。每次最多 200 行或 256 KiB。 | snapshot、候选列表、index/worktree identity 或 blob pair 改变时返回 stale,不返回来自另一版本的后续 diff 页。 |
稳定 cursor
{
"snapshot_id": "snap_...",
"query_kind": "directory | project_search | review_scope | review_diff",
"owner": {"project_id": "...", "session_id": "..."},
"basis": "directory/version or journal/Git revision",
"filter": {"query": "...", "category": "...", "sort": "..."},
"cursor": "opaque token",
"next_cursor": "opaque token | null",
"page_size": 100
}cursor 不变量
- cursor 只在创建它的 snapshot、owner、query kind、过滤条件和排序完全一致时有效;服务端不接受客户端提供的 offset、路径锚点或跨项目 token。
- snapshot 保存候选集的稳定顺序或足以重建该顺序的不可变 revision;后续页绝不重新扫描后混入新增、删除或重排的项。
- snapshot 可受容量和 TTL 限制。逐出或内容版本改变时明确返回 stale,由客户端刷新首屏;不得悄悄改用最新列表继续旧 cursor。
- 目录和搜索各自使用 cursor;Review 文件列表与单文件 diff 使用不同 cursor namespace,不能互换。
工作区审查快照
{
"snapshot_id": "workspace_...",
"project_id": "...",
"head_identity": "commit/tree identity",
"index_identity": "index tree identity",
"candidates": [{
"path": "canonical relative path",
"status": "XY or ??",
"base": {"kind": "blob | absent", "digest": "...", "mode": "..."},
"index": {"kind": "blob | absent", "digest": "...", "mode": "..."},
"worktree": {"kind": "regular | absent", "digest": "...", "mode": "...", "identity": "lstat fingerprint"}
}]
}Workspace 不变量
- 创建 snapshot 时一次性确定未提交 candidate list,按规范化相对 path 排序;它包含 staged、unstaged、untracked、rename/delete 的所有相关 path,且保留旧路径和新路径的对应关系。
- 每个 candidate 固定 HEAD base、index blob 和 worktree content identity/blob pair。文本 diff 只能从该 pair 渲染,不能在后续请求中重新执行最新
git diff代替。 - 请求下一页或单文件 diff 前,服务端重新读取 status candidate vector、index tree identity 和目标 path 的 no-follow worktree identity;任一新增、删除、状态、mode、index blob、worktree digest 或路径映射变化均返回
STALE_SNAPSHOT。 - snapshot 保存的 content blob 受同一 review storage quota 管理;blob 缺失、超限或过期也返回 stale/unavailable,绝不从当前 worktree 近似补齐旧 diff。
所有文件 WebSocket 请求
| 规则 | 合同 | 禁止 |
|---|---|---|
| 必填关联键 | 所有文件浏览、读取、写入、创建、重命名、复制、删除、reveal、turn summary、turn diff、Review scope 与 Review diff 请求都带客户端生成的 UUID request_id;成功、stale、validation error 和 transport error 都原样回显。 | 不接受缺失或非 UUID 的文件请求;不再以 response type、path 或到达顺序认领响应。 |
| 响应验证 | 客户端以 request_id 作为唯一 pending key,并额外验证 action、project/session、snapshot 与目标 path;组件卸载、项目切换或新查询会取消并遗忘旧 pending key。 | 不得用全局请求队列消除竞态;一个慢目录请求不能阻塞另一个文件 read 或 Review diff。 |
| 写入幂等与冲突 | 有副作用的文件 action 还携带独立 idempotency_key 与现有的 optimistic-lock 条件。服务端以 action、owner、canonical payload 和 precondition fingerprint 建立 durable record。request_id 仅关联本次传输,不替代写入幂等键。 | 不把重连后的新 request id 当作新写入;不以缓存 mtime 覆盖服务端冲突结果。 |
| 错误形状 | 每个结果提供 request_id、action、owner、status 和稳定 error code;STALE_SNAPSHOT、NOT_FOUND、CONFLICT 与 INVALID_REQUEST 可被 UI 明确处理。 | 不吞掉缺少 request id、错误 owner 或未知 response 的帧,也不将其更新到当前文件状态。 |
写操作 idempotency_key
{
"record_key": {"owner": "profile + project", "action": "project_file_write", "idempotency_key": "uuid"},
"fingerprint": "sha256(action + owner + canonical payload + precondition)",
"state": "prepared | in_flight | completed",
"operation_id": "fileop_...",
"result": "canonical terminal result or null"
}幂等不变量
- owner 是经授权的 profile 与 project;canonical payload 使用规范化根内 path、标准化 operation 参数和内容 digest,排除
request_id与idempotency_key。precondition fingerprint 包含 expected mtime/digest、源与目标路径、overwrite 条件及影响目录的版本条件。 - 服务端在与文件 mutation 同一 durable scope 中原子领取
(owner, action, idempotency_key)。同一 record key 的 fingerprint 不同,返回IDEMPOTENCY_KEY_CONFLICT,零文件写入。 - 同 fingerprint 的 in-flight 重试绑定既有
operation_id,等待既有操作到本次请求的响应期限;未完成时返回IN_PROGRESS和该 operation id,客户端只能重试同一个 key,不能另发写入。 - 完成后 durable record 保存 canonical terminal result。相同 fingerprint 的任何重试只重放该结果,即使 request_id 不同,也不再次执行文件 mutation。进程重启先恢复或判定 in-flight record,再允许其结果被重放。
- record retention 至少覆盖客户端重试窗口与 worker restart recovery;过期清理不得删除仍为 in-flight 的 record,且不会将已过期 key 解释为一个不同 fingerprint 的成功重放。
缓存与未保存草稿
| 存储 | 键与容量 | 淘汰与一致性 | 数据保护 |
|---|---|---|---|
| 读取缓存 | 键为 project_id + canonical relative path + content revision;最多 64 项且最多 16 MiB,使用访问顺序 LRU。 | 只缓存成功且完整的只读结果。写入、rename、delete、tree revision 变化和容量超限会失效或淘汰最旧的干净 read;缓存未命中只触发新的 request_id 请求。 | 缓存不是编辑状态,也不保存 dirty text。 |
| 未保存草稿存储 | 浏览器原生 IndexedDB 的 openprogram-file-drafts 数据库,键为 project_id + canonical relative path;持久化 UTF-8 draft、baseline content revision、baseline mtime、保存状态与更新时间。容量上限为 32 个 draft 或 8 MiB。 | 干净 draft 可删除;dirty draft 不参与 LRU 淘汰。所有 entry 与 aggregate byte/count index 在一个 readwrite transaction 中更新。 | 页面刷新、tab unmount、切换项目和 read-cache 淘汰都不能删除 dirty draft。服务端文件变化后保留草稿并标记冲突,保存必须继续提交其原始 baseline 条件。 |
| 路径变更与清理 | 成功 rename 在 IndexedDB transaction 中移动同项目 draft key;copy 不共享 source draft。删除文件先列出 dirty draft,要求明确的保存、保留/导出或丢弃选择。项目 draft 清理只由未来显式 project unlink/delete command 触发;当前产品没有该 action。 | 任何文件操作失败时不变更 draft key。确认 discard 后,文件 draft 与 project aggregate index 在单个 IndexedDB transaction 中清理;清理失败保留原记录并报告失败。不得把 list_projects 中项目消失解释为用户授权。 | 跨项目的同名 path 永不共享 cache、draft、selection 或 search snapshot;不因 tree 刷新、Review 切换、临时 registry unavailable 或后台响应重置编辑器 buffer。 |
IndexedDB dirty draft 合同
{
"database": "openprogram-file-drafts",
"stores": ["drafts", "project_index"],
"draft": {
"key": "project_id + canonical relative path",
"draft_utf8_bytes": 0,
"baseline_utf8_bytes": 0,
"baseline_revision": "...",
"baseline_mtime": 0,
"updated_at": "local monotonic ordering"
}
}持久化与恢复不变量
- 使用浏览器原生 IndexedDB,不增加依赖。配额计算使用
TextEncoder的 UTF-8 byte length,计入 draft、baseline 与所存元数据;project_index保存每项目的键集合、count 和总 bytes。启动时以 project_index 恢复该项目的 dirty drafts;索引缺失或不一致时扫描 drafts 重建索引,不删除记录。 - 新建、更新、rename、discard 与未来显式 project cleanup 都在一个 IndexedDB readwrite transaction 内先计算新旧 bytes,再同时写 draft 与 aggregate index。超出 32 项或 8 MiB 时 transaction 不写入,现有 draft 保持不变。
- 编辑器在接受会把持久化 draft 超出配额的输入前先检查预算;满额时阻止创建新 dirty draft,并显示可见的保存、还原、导出或显式丢弃提示。已有 dirty draft 不自动淘汰;更新已有 draft 遇到浏览器
QuotaExceededError时保留最近成功持久化版本、显示持久化失败状态并暂停进一步会扩大该记录的编辑,直到用户处理空间或显式导出/丢弃。 - 删除项目文件时,若对应 dirty draft 存在,服务端删除完成前 UI 必须要求保存、导出保留或明确 discard;成功 discard 才清理该 key。当前没有 project unlink/delete action,因此项目 draft 不因 registry snapshot 缺少该项目而清理;未来显式 command 必须先展示该项目 draft 数量和 bytes,确认后清理所有该项目 keys 与 index;取消或失败不清理。
- draft 内容仅保存在当前浏览器 origin 的 IndexedDB;不进入日志、遥测、模型上下文、WebSocket payload 或跨设备同步。显式 discard、未来确认的 project cleanup、浏览器站点数据清除和应用卸载会删除本地内容;恢复时不得自动上传或重新写回项目文件。
Mutation journal 数据合同
{
"transaction_id": "mtx_...",
"session_id": "...",
"workspace_id": "workspace_...",
"workspace_sequence": 184,
"branch_ref": "branch_...",
"lineage_head_id": "...",
"producer_turn_id": "actual assistant message id",
"origin_turn_id": "user-visible parent turn id",
"actor_id": "main | child_...",
"job_id": null,
"worktree_id": null,
"kind": "edit | revert | rewind",
"status": "prepared | applied | committed | aborted | rolled_back | recovery_required",
"files": [{
"file_lineage_id": "file_...",
"path": "canonical absolute path",
"operation": "create | modify | delete | rename",
"old_path": null,
"before": {"kind":"regular","digest":"sha256:...","blob_ref":"blob_...","mode":"0644"},
"after": {"kind":"absent"},
"stats": {"added":16,"removed":4,"binary":false},
"diff_state": "available | large | binary | timeout",
"recoverability": "exact | unavailable",
"unavailable_reason": null
}]
}不变量
- 原始写入先持久化 prepared receipt,再写文件,确认 after state 后转 committed;prepared 持久化失败时 trusted mutator 必须在写盘前失败。
{kind:"absent"}只表示已精确确认路径不存在;baseline 获取失败只能由recoverability=unavailable和 reason 表达,永远不能编码成 absent。recoverability=unavailable只用于 journal 已成功持久化、但因明确大小或文件类型策略不保存 recovery blob 的 mutation,不用于吞掉 journal 故障。- 每个 user prompt 定义
before_turn;assistant 完成后定义after_turn。UI 必须写清恢复哪一个边界。 - 同一 turn 多次修改同一 file lineage,摘要折叠为 first-before 到 last-after 的净变化;rename 后仍保持 lineage。
- 所有共享 checkout 的 transaction 在 workspace lock 下领取全局单调
workspace_sequence;多轮投影按 sequence 和 lineage,不按 DAG seq/DFS。 - before/after regular state 都保存 blob ref,才能稳定显示历史 diff、恢复和条件式 Reapply。
- 路径在 capture/plan/apply 时都重新规范化;保存并复检父目录链 identity、leaf lstat、device/inode 和 hardlink count,使用已验证 directory handle 或等价 no-follow 原语。
- Current branch 只折叠 active lineage 的 transaction,不混入 sibling branch。
- Revert/Restore/Reapply 本身也写 mutation transaction,原记录保持不可变。
Checkpoint 与 WorkspaceRevision
| 对象 | 必需字段 | 用途 |
|---|---|---|
| 分支 | 稳定 branch_id、parent branch、fork checkpoint、HEAD、head_version、writer epoch;session 另存 active_branch_id。 | source branch HEAD 永不后移。Restore/Branch 确认时立即创建新 branch ref;纯历史浏览只改变 navigation cursor。 |
| Checkpoint | checkpoint_id、boundary=before_turn|after_turn、user/assistant node、branch id、journal sequence、workspace revision。 | 定义恢复到哪里;不重复保存一套文件列表。 |
| ChangeSet | producer turn、origin turn、branch、actor/job/worktree、transaction sequence、净 file lineage 变化。 | Turn card 通过 origin 分组,但按 change set id 去重并保留真实生产者。 |
| WorkspaceRevision | revision id、受管路径投影 digest、对应 branch/checkpoint、alignment 状态。 | 检查 branch checkout、conversation-only restore 和实际 workspace 是否一致。 |
| RestorePlan | plan hash、idempotency key、expected HEAD/revision、目标 checkpoint、文件 actions、冲突。 | 预览与确认之间防止 TOCTOU;重复请求不得执行两次。 |
RestoreIntent / RestoreAction
{
"restore_intent_id": "restore_...",
"idempotency_key": "...",
"plan_hash": "sha256:...",
"source_branch_id": "branch_source",
"target_branch_id": "branch_target",
"expected_head_version": 42,
"expected_workspace_revision": 184,
"target_checkpoint_id": "cp_...",
"mode": "code_and_conversation",
"status": "prepared | applying | committed | aborted | rolled_back | recovery_required",
"actions": [{
"action_id": "ra_...",
"file_lineage_id": "file_...",
"path": "/canonical/path",
"expected_current_digest": "sha256:...",
"target_digest": "sha256:...",
"target_blob_ref": "blob_...",
"rollback_digest": "sha256:...",
"rollback_blob_ref": "blob_...",
"applied_digest": null,
"state": "pending | applied | verified | rolled_back",
"error": null
}]
}恢复记录不变量
- Plan 是只读预览;Intent 是确认后的持久执行记录,两者由 plan hash 绑定。
- 每个 action 在文件替换前后分别持久化状态;重启不通过目录内容猜测执行位置。
- Rollback 只在 current digest 仍等于该 action 的 applied digest 时写回 rollback blob。
- 任何 action 无法安全 rollback 时,intent 保留每个路径的真实状态并进入 recovery_required。
- target branch 只在所有代码 action verified 后激活;source branch ref/head 始终不变。
三个 Review scope
| 范围 | 事实源 | 包含 | 恢复能力 |
|---|---|---|---|
| 本轮 | 指定 origin turn 的 committed change sets | 父 turn 直接 change set + owned child change sets;child 始终保留独立 producer_turn_id,按 transaction id 去重并显示 actor。 | 所选 causal closure 全部 receipt exact、所有 actor terminal 且 current digest 匹配时可 Undo/Revert。 |
| 当前分支 | 当前 active lineage 的增量 mutation projection | 所有 turn、显式 revert/rewind/reapply;不包含 sibling branch 和外部写入。 | 从具体 turn/checkpoint 发起,不提供无范围的一键 branch Undo。 |
| 工作区 | Git 跟踪差异 + git ls-files --others --exclude-standard | 当前未提交变化;遵循 .gitignore。内部再切换 Unstaged、Staged、Branch 或 Commit。 | 无 turn Undo。Git Discard 与 checkpoint Restore 使用不同入口和确认文案;非 Git明确 unavailable。 |
多轮 Rewind:一次规划,一次事务
每轮同时有 before_turn 与 after_turn checkpoint。User message 的 Restore 入口默认选择“恢复到本次请求之前”;assistant turn card 可选择“恢复到本次回复之后”。目标 suffix 只沿 source branch predecessor chain 计算,不碰 sibling branch。Source branch ref 和 HEAD 始终保留;conversation 模式立即创建新的 target branch ref,并在协调事务提交后切换 active branch。
选择 checkpoint 和恢复模式。
冻结 expected HEAD、workspace version 与 suffix。
展示 turn/file 影响、不可恢复项和 branch 结果。
冲突、缺 blob、运行中任务或不安全路径;零写入。
写 durable intent,锁 workspace,stage、apply、verify。
追加 transaction;需要时移动 HEAD;刷新 UI。
规划算法
| 阶段 | 规则 | 失败结果 |
|---|---|---|
| 解析分支沿袭关系 | 从当前 HEAD 沿 conversation predecessor 回到目标 user checkpoint;caller subtree 只按明确 ownership 纳入。 | 目标不在 active lineage:拒绝,提示先切换分支。 |
| 合并目标状态 | 按 journal 投影 checkpoint 时每个路径的目标状态;同文件跨多轮只生成一个最终 restore action,不逐轮覆盖。 | journal 不连续或历史 receipt 不精确:代码模式 unavailable,对话模式仍可选。 |
| 预检 | 从全局 current_workspace_revision 规划到 target checkpoint,只比较实际 plan actions 涉及的 file lineage;检查 blob、父目录/leaf identity、file type、root、空间和权限。 | 任一失败进入 BLOCKED,列出每个路径,零文件改变。conversation/workspace mismatch 本身不是 source digest。 |
| 准备意图 | 保存 plan id、expected branch/head version、workspace revision、before images、目标 images、writer epoch 和恢复模式;请求幂等。 | 确认前状态变化返回 stale-plan,重新预览。 |
| 应用文件 | 获取 workspace mutation lock;每个 action 持久化 pending/applied/verified/rolled_back 和本 transaction applied digest;逐文件原子替换,删除仅在 current digest 匹配时执行。 | 回滚前再次确认 current digest 仍等于本 transaction applied digest;外部 actor 已改写时不覆盖,进入 RECOVERY_REQUIRED。 |
| 创建对话分支 | conversation-only 或 code+conversation 先准备新的稳定 target branch id/ref,source branch HEAD 不变;首轮前目标统一为持久 synthetic ROOT,禁止 head_id=None fallback。 | 创建/激活使用 expected source head_version CAS;失败则按 intent 恢复文件并保留 source/active branch。 |
| 完成 | 追加 rewind transaction 和 navigation event;不把旧节点全局写成 rewound。提升 branch writer epoch,使旧异步任务无法回写当前 branch。 | 重启时扫描未终态 intent,完成或回滚,不猜测成功。 |
恢复模式
| 模式 | 文件 | 对话 HEAD | 后续行为 |
|---|---|---|---|
| 代码 + 对话 默认 | 恢复到 checkpoint | 立即创建 target branch,HEAD 指向目标 user prompt 之前;提交后激活 | 原 prompt 回填;source branch ref/head 保留。 |
| 仅对话 | 保持当前 | 立即创建并激活 target branch | 显示 workspace mismatch。用户确认 Keep current files 后追加 adopt_workspace_revision event 和新的 branch checkpoint;之后从该明确基线继续。 |
| 仅代码 | 恢复到 checkpoint | 保持当前 | 追加 rewind transaction;显示“代码已回退、对话仍在最新位置”。 |
分支、Checkout 与 sub-agent
| 场景 | 目标语义 | 文件规则 |
|---|---|---|
| 从助手结果创建分支 | 立即创建稳定 target branch id/ref,HEAD 指向该 assistant,并切换 active_branch_id;source branch HEAD 保持原位置。 | 默认不修改文件,按钮副标题明确“Files stay as they are”。纯浏览历史只移动 navigation cursor,不创建/移动 branch。 |
| 分支为新任务 | 创建 source-preserving 新 task/thread,原 task 和 HEAD 不变;可选择复制 conversation only 或携带 checkpoint 文件状态。 | Git 项目中“with files”优先创建隔离 worktree 后物化 checkpoint;非 Git 不提供无提示目录复制,改用同 task 的显式 Code + conversation Restore。 |
| 切换同级分支 | 切换当前可见与模型使用的 conversation lineage。 | 计算 branch/workspace alignment。浏览允许;开始文件修改前必须选择保持当前 workspace 或恢复该 branch 的代码投影。 |
| Owned child(前台或后台) | 判据是 relation + creates_agent + origin_turn_id,不是前台/后台。child 始终有独立 producer turn/change set,父 turn 只按 origin 分组。 | 运行中时阻止父 restore;完成后进入父级 owned causal closure,plan 逐个列出并按 transaction id 去重,不能靠 caller DFS 猜测。 |
| 独立执行者 | 无 owned relation 的 background terminal、job runner、channel 和 scheduler 注册独立 workspace writer identity。 | 运行中时阻止同 workspace restore;完成回写必须校验 branch writer epoch,旧任务不能在 restore 后重新写入当前 branch。 |
| 已有对端 / 跨会话调度 | agent(to=existing)、send message 和既有 peer 是 linked collaboration,不是父 turn 独占后代。 | 父 restore 默认不修改 peer 的对话或 change set;impact sheet 单独列出 linked actor,重叠 workspace/path 时阻止 code restore。 |
| Worktree 子任务 | 文件状态属于独立 worktree。 | 父 workspace 的 Undo/Rewind 不触碰;merge/discard 使用 worktree 自己的事务和冲突语义。 |
| Worktree 合并 | merge 产生独立 mutation transaction,归属于执行 merge 的 producer turn。 | 父 workspace 只从 merge transaction 恢复;不依赖可能已删除的 child worktree checkpoint。 |
| 同级分支 | Rewind 只作用于 active lineage suffix。 | 不改变 sibling journal;切回 sibling 时重新计算 alignment。 |
Resume、Archive 与 Purge 不变量
| 操作 | 合同 | 禁止 |
|---|---|---|
| 继续会话 | 未知 session 返回 not-found;先恢复所有 prepared/applied/recovery-required transaction,再加载真实 node ids、active_branch_id、branch HEAD/version 和 workspace revision。 | 不能把未知 ID 当新 session;不能生成 loaded-<index> 伪 ID。 |
| 继续空分支 | 首轮前 HEAD 指向持久 synthetic ROOT;Web/TUI/Rich CLI 使用同一 branch result schema。 | 不能用 persisted_head or tip 或 head_id=None 自动选择其他 tip。 |
| 归档分支 | 默认删除操作只 archive branch ref,保留 nodes、journal、checkpoint refs 和重新激活能力。 | 不能删除共享祖先或把 predecessor 留成意外新 tip。 |
| 清除分支 | 显式高级操作;仅释放没有其他 branch、checkpoint、operation、audit 或 active intent 引用的独有对象,使用引用计数 GC。 | 不能按 leaf 向后删一个节点,也不能让 blob GC 绕过引用。 |
冲突、不可恢复与并发状态
Revert historical turn 不尝试自动反向合并;Rewind 预检 current digest。任何不匹配都阻止整次文件操作。
summary 和 diff metadata 仍可 Review;Code restore 选项 disabled,Conversation only 保持可用。
operation、path、size 和 recoverability 必须精确;只有工具 delta 或预算内 before/after 可得时行数才精确,否则 added/removed 显示 unknown。不可 Undo,也不能把整文件算成新增。
不跟随、不覆盖;显示路径和原因,要求用户通过原工具或版本控制处理。
同 workspace mutation lock 未释放时不生成可确认 plan;用户先停止或等待任务。
apply 校验 expected HEAD、workspace version 和 plan token;返回 stale-plan 并刷新影响,不执行旧 plan。
启动时读取 durable intent 和每个路径状态;能安全回滚则 ROLLED_BACK,外部改写导致回滚会覆盖新内容时保留现场并进入人工恢复。
This turn 与 Current branch 精确 journal 可用;Workspace scope 显示 unavailable,不进行目录扫描。
既没有 mutation receipt,也被 Git exclude-standard 排除,不出现在 turn card 或默认 Workspace Review。
Trusted mutator 的 receipt 仍进入 journal,可 Undo/Restore;它只是不出现在 Git Workspace scope。普通 Bash 或无 receipt 的 ignored 变化仍排除。
仅当 current digest 等于 revert 后状态、没有后续同 file lineage mutation、after blobs 仍存在时可用;执行后追加新的 transaction。
数据库、API、部署、包安装、Git index/commit、运行进程、网络请求和未记录 Bash 不在文件 Restore 范围内;确认页逐项提示可能重复。
UI 信息架构与操作入口
| 位置 | 显示 | 操作 | 禁止 |
|---|---|---|---|
| 助手轮次卡片 | 精确文件数、净增删、前三个文件;完整外框和约 40px header。左侧使用用户指定的 19px FeatherIcon 表示本轮包含直接文件编辑,不使用网站 logo。总增删数字始终显示 additions 与 deletions 两项,包括零值;其下以 2.5px 双色比例条编码两者的真实占比。操作组、按钮和 Undo 图标不参与 flex 收缩,方向图标使用 1em 与按钮文字等高,标题区域承担截断;≤420px 时标题缩写为 “N files”,FeatherIcon、统计和操作保持可见,header 固定 40px。header 与文件列表之间保留一条分界线;目录弱、basename 强,统计固定列宽,分类只在 Review 中出现。 | 最新可恢复 turn:Undo;成功 Undo 后且重新预检可用:Redo;历史 turn:Revert;历史 Revert 后且重新预检可用:Reapply;所有 turn:Review。Undo / Revert / Redo / Reapply 使用文字加方向图标,Review 使用中性色描边按钮。header 只显示当前可执行动作,unavailable、blocked 与 Reverted 状态只在 footer 说明。 | Redo 与 Reapply 都写新的 reapply transaction,不维护独立线性栈;不显示 header chips、禁用状态标签或参考产品品牌元素。 |
| 用户消息页脚 | 现有消息动作中的 Restore checkpoint 入口。 | 打开 checkpoint impact sheet,明确 before_turn/after_turn,默认 Code + conversation。 | 不在点击后立即恢复;必须先显示影响、可恢复性和外部副作用。 |
| 助手消息页脚 | Branch 动作。 | Branch from here,副标题 Files stay as they are。 | 不暗中恢复 checkpoint。 |
| 中心变更标签页 | This turn / Current branch / Workspace;Workspace 内含 Unstaged / Staged / Branch / Commit;左文件 rail、右单文件 diff;来源标签始终可见。搜索与分类显示完整过滤结果的总数,页码只表示该结果集。 | Unified / Side-by-side、行级评论、Open file、Reveal。改变 scope、搜索、分类或排序时清空 cursor、selection 和 diff;File 使用现有虚拟化 editor tab,不在 Review 内复制整文件。 | Workspace scope 不显示 turn Undo;一次只挂载一个有界 diff;不能在客户端只过滤已加载的一页。 |
| 历史影响面板 | 目标 prompt、撤回 turn 数、文件数、branch 结果、恢复模式、冲突和 unavailable 原因。 | 确认 Rewind、Conversation only、Code only;取消无副作用。 | blocked 时无 Force 按钮;高级手动恢复从 Review 处理。 |
| 对齐提示条 | Conversation branch 与 workspace projection 不一致的原因。 | Keep current files 或 Restore branch code。 | 不能只显示警告后让 mutating turn 静默开始。 |
性能、保留、安全与兼容
复杂度只与 committed changed paths 有关;不遍历 workspace。摘要在 finalize 持久化并随 transcript 返回。
服务端先筛选和稳定排序,文件列表每页 100;客户端只保留当前页和相邻页。选中文件后每批最多 200 行或 256 KiB,并支持继续加载。
计算超预算返回 timeout metadata;不阻塞 Agent 完成,也不回退到 before 与当前磁盘的跨轮近似 diff。
目录、项目搜索和 Review 的分类、搜索、排序与 opaque cursor 都绑定同一 snapshot;workspace 或项目变化后旧页返回 stale 并整体刷新,不能混页。
每个文件 WS 请求有 UUID request_id;切文件或切项目取消旧请求。迟到响应还必须匹配 owner、scope、snapshot revision、change set 与 blob pair。
上千文件预检作为可观察后台任务,冲突结果分页;进入 APPLYING 后不可取消,只能完成或事务回滚。
使用独立内容寻址存储,按 digest 去重,权限限于本地用户;不进入 session Git。默认保留最近 100 个 checkpoint 和每个 file lineage 的首个 baseline。
Pinned、活动 task、未解决冲突、prepared/applying intent 和仍被 branch ref 引用的 checkpoint 不回收。其余 blob 到期后显示 Review available / Code restore unavailable。
blob 不进入日志、遥测或模型上下文;session 删除一并清理无引用 blob。外部路径必须有当时和恢复时的明确授权。
Read cache 受 64 项 / 16 MiB LRU 限制;dirty draft 独立持久化,受 32 项 / 8 MiB 配额保护且绝不自动淘汰。
旧 checkpoint 只用于只读 Review 或明确标记 unavailable;没有 digest/recovery contract 时不提供新 Undo/Rewind code 模式,也不保留无 request_id 的兼容入口。
/rewind 先展示同一 server plan,再选择模式并确认;不能继续直接调用旧 rewind_to。
同一 plan/apply 协议和幂等 request id;UI 只渲染 server 返回的 canonical impact,不在客户端重算。
绑定 change_set id、before/after blob pair、side 与 hunk/line;scope、branch 或 restore 变化后标 stale,不能发送到另一版本。
稳定模块职责与兼容移除
| 模块 | 最终职责 | 不承担的职责 |
|---|---|---|
apps/server/.../ws_actions/files.py | 项目根授权、目录 listing、project search、read 与写入类文件操作;统一校验 project/path、request_id、idempotency key 和目录/search snapshot。 | 不计算 turn/branch Review,不保存浏览器缓存或草稿。 |
apps/server/.../ws_actions/turn_files.py | 从 mutation journal 与 Git 生成 Review scope、过滤后的分页结果和绑定 snapshot 的单文件 diff;统一回显 request_id。 | 不提供项目文件树搜索,不让 turn diff 绕过 Review snapshot。 |
apps/web/lib/net/ws-request.ts | 创建 request_id、维护按 id 的 pending registry、超时/取消和严格响应验证。 | 不以 response type 匹配第一个响应,不维护文件请求的全局串行队列。 |
apps/web/lib/files/files-shared.ts | 以 project-scoped key 管理有界 read cache、持久 dirty draft store 与显式失效事件。 | 不作为组件间的无界内容仓库,不保存 Review page 或 socket listener。 |
FileTree / FilesPage | 渲染目录、搜索和文件操作意图;仅消费服务端分页与 project search 结果。 | 不扫描本地已加载节点来代表项目搜索,不自行重排跨页结果。 |
ReviewTabPane | 保存当前 scope/filter/snapshot/cursor/selection,渲染服务器确认的单页与单文件 diff。 | 不对当前页二次过滤来实现 category/search,不复用另一 scope 的 diff。 |
FileTabPane | 编辑器 buffer、保存冲突提示和 dirty-draft 生命周期;从项目文件读取 canonical 内容。 | 不直接调用旧 turn diff 入口;历史文件比较统一由 Review tab 打开。 |
filesWsRequest 全局队列、缺少 request_id 的文件 action、FileTabPane 直接调用的 turn_file_diff 入口,以及把 list_turn_files 当作新消息摘要回填的路径。旧 session 没有可用的 journal/review snapshot 时返回只读 unavailable;不为旧入口保留会绕过新合同的 fallback。实施 brief 与验收矩阵
A–J 已完成并通过独立 specification review 与 quality review。每个新增任务实现后也必须通过相同审查,才能更新本页状态。
| 任务 | 生产边界 | 必须先失败的 public-entry 场景 | 排除 | 状态与证据 |
|---|---|---|---|---|
| A · 精确修改日志 | programs/tools/files/、turn context/finalize、checkpoint manifest/store。 | 已有 >5 MiB 文件首轮修改不算全新增;Bash ignored log 不进入 turn;失败 edit 无 receipt。 | 不实现 Review UI,不改用户 Git。 | 审查通过 public-entry、受影响测试与静态检查均已验证。 |
| B · 安全单轮撤销/还原 | agent/internals/_revert.py、workspace lock、durable intent。 | 后续同文件修改时零写入;创建文件 digest 不符时不删除;中途 I/O 失败不部分标记。 | 不实现 multi-turn HEAD move。 | 审查通过 public-entry、受影响测试与静态检查均已验证。 |
| C · 多轮规划/应用 | agent/_rewind.py、session head API、intent recovery、WS/CLI protocol。 | 同文件跨三轮恢复到目标;file failure 不移动 HEAD;sibling branch 不受影响;stale plan 零写入。 | 不重写 compaction 或 DAG schema。 | 审查通过 public-entry、受影响测试与静态检查均已验证。 |
| D · 审查范围与界面 | ws_actions/turn_files.py、message metadata、center tab/store、chat card。 | 历史 transcript 不逐条请求;Workspace ignored 排除;一次只挂载一个有界 diff;三 scope 来源一致。 | 不加第三 pane、新窗口或参考产品品牌。 | 审查通过 public-entry、受影响测试、TypeScript 与 UI gate 均已验证。 |
| E · 分支对齐与多 agent 归属 | checkout/branch UI、active-lineage fold、owned/linked/worktree ownership。 | conversation-only checkout 后 mutating turn 不静默开始;owned background child 运行时阻止父 restore、完成后按 origin 进入 causal closure;linked peer/cross-session 不被父 restore;worktree child 不改父 workspace。 | 不实现通用 workspace cloning。 | 审查通过 public-entry、受影响测试、TypeScript、ruff 与 WS action gate 均已验证。 |
| F · 项目查询快照 | ws_actions/files.py 的目录分页、project search、路径授权与 snapshot registry;FileTree 的结果状态。 | 未展开目录中的命名文件能被搜索;第一页后修改目录不会让同一 cursor 混入新项;cross-project cursor 被拒绝。 | 不更改视觉布局、不引入全文内容索引或第三方搜索服务。 | 审查通过 目录与搜索使用 stable cursor、同一 snapshot 的增量分页和有界结果。 |
| G · 文件请求身份 | 文件类 WS dispatch、ws-request.ts、durable idempotency record 与所有文件/Review 调用者。 | 两个同 action 请求可乱序返回且分别更新正确调用者;缺失 request_id 被拒绝;相同 idempotency_key 的 in-flight retry 绑定原 operation,完成后重放结果;相同 key 而 fingerprint 不同则冲突且零写入。 | 不改变非文件 WS action 的协议,不把 request_id 当作写入幂等键。 | 审查通过 request identity、durable mutation、断线重试、worker lease 与有界 pending registry 已验证。 |
| H · 有界缓存与持久草稿 | files-shared.ts、原生 IndexedDB draft store、FileViewer、FileTabPane、tab close/rename/delete 生命周期;project unlink/delete cleanup hook 保留到产品提供该显式操作时接入。 | 超过 read-cache 限额后只淘汰干净 LRU read;刷新、tab 切换和项目切换后 IndexedDB dirty draft 仍存在;quota/浏览器 QuotaExceededError 时旧 draft 与 aggregate index 原子保留,满额阻止新 dirty 并显示提示。当前没有 project unlink/delete action,不从 list_projects 消失推断用户授权。 | 不引入协作编辑、远端草稿同步或无限离线文件副本;不新增 project remove UI/action。 | 审查通过 64 项 / 16 MiB read cache、32 项 / 8 MiB durable draft store、生命周期补偿与恢复合同已验证。 |
| I · 筛选后审查分页 | turn_files.py 的 filter/sort/snapshot/page,以及 ReviewTabPane 的 query state。 | 分类结果跨页完整;改变 category/query/sort 后旧 cursor 与 selected diff 失效;Workspace snapshot 固定 staged/unstaged/untracked candidate list 及每文件 blob pair,随后 index/worktree 任一变化返回 stale。 | 不增加第三 Review pane,不改变 mutation journal 或 Git 的事实源。 | 审查通过 服务端先筛选后分页,Review cursor 绑定冻结 snapshot 并检测 stale。 |
| J · 规范入口切换 | 文件模块调用图、旧 adapter 删除、legacy-session unavailable 呈现。 | 没有 filesWsRequest 全局队列、无 request_id 文件 action 或直接 turn_file_diff 的 UI 调用;旧 session 不会走 bypass fallback。 | 不为旧入口保持隐藏兼容开关,不删除可验证的只读历史元数据。 | 审查通过 canonical request path、旧 action/fallback、临时 registry 已移除;服务端与前端按稳定职责完成无环拆分。 |
| K · 轮次卡片空状态与失败状态 | assistant-bubble.tsx、message-rail.tsx、turn-files-chips.tsx 与卡片 CSS。 | 普通文字回复不挂载卡片或 Review;旧文件写入轮次按需查询,空结果隐藏;查询失败只显示可重试的紧凑状态。 | 不改变 mutation journal、Review scope、diff 或恢复协议。 | 审查通过 empty/error/retry、stale response、rail round-trip、TypeScript、Web 单测、静态检查、生产构建与文档 gate 均已验证。 |
完整可见验收
搜索命中一个未展开深层目录中的文件;并发 create/rename 后继续旧 cursor 必须返回 stale,刷新后结果按项目内稳定顺序重新开始。
目录 read、file read、write、rename、turn summary、Review list 和 Review diff 同时发出并乱序返回。每个 response 只能被匹配的 pending request 接受;同 idempotency key 的 write retry 只关联同一 operation 并重放完成结果。
填满 read cache 与 IndexedDB draft quota 后刷新页面、切换 tab 和项目。已有 dirty draft 可恢复并保留原 baseline;新 draft 或扩大 draft 在无空间时不提交 transaction,并显示可见处理提示。
立即创建并激活稳定 target branch,source branch ref/head 不变;文件保持当前,alignment banner 出现,下一次文件修改前要求决策。
HEAD 不动,追加 rewind transaction,Current branch Review 反映新状态。
确认时 target branch ref 立即存在并激活,source branch HEAD 不变;文件保持当前。下一条消息只是延长已创建的 target branch。
新分支绑定明确 before/after checkpoint;保持当前文件或恢复目标文件由用户选择,不从旧运行结果静默继续。
历史 Revert 仅在无后续同 lineage 依赖时成功;Reapply 再次预检并写新 transaction。
超过一页的 Code、Tests、Docs 和 Large 结果分别先在服务端过滤;切换分类、query 或排序后旧页和已选 diff 均不再显示。Workspace 创建 snapshot 后修改 index、worktree 内容或候选列表,后续页和 diff 均返回 stale。
OpenProgram background task、其他 session 或外部进程改变文件时计划失效或 apply 后校验回滚。
在每个 intent phase 强制终止并重启,最终必须是 COMMITTED、ROLLED_BACK 或持久化到具体路径的 RECOVERY_REQUIRED;永不假报 committed,也不覆盖外部新写入。
从用户可见文件入口检查调用图:没有临时 queue、缺 request_id action 或 direct turn diff fallback;无法按新合同解释的旧 session 显示只读 unavailable。
可选 Office 安装已实现并通过独立规格与质量审查。最终受影响配置、适配器、安全与仓库契约范围内1812项测试通过(82项可选依赖跳过,1项预期失败)。全部22项原生 Office/浏览器测试与710项 Web 测试通过;Web 构建、TypeScript、静态检查、桌面检查和文档链接通过。显式同意浏览器用例验证取消不安装、失败重试与自动打开文档;组件测试验证不可变发布、origin 拒绝和并发请求去重。托管 HTTP 客户端真实访问固定来源成功,未下载归档正文。
实现状态
连续 PDF 阅读器、文本选择、剪贴板规范化、大纲与缩略图导航、高亮和自由文本批注已实现。公开浏览器测试在原生与桌面兼容环境中验证渲染、复制、批注序列化、撤销/重做与保存失败后的草稿保留。独立规格与质量审查通过。公开回归还验证恢复输入限制、极端纵横比缩略图有界性与清除搜索高亮。完整源码验证清单通过,包括真实浏览器测试、Web 测试、契约、文件组件测试、构建、类型、静态资源与文档检查。已安装 App 验收与源码验证分别记录。
PDF 运行时兼容性使用匹配的上游 legacy 模块,并在真实应用策略下允许本地 blob 字节读取。公开 FileViewer 测试在原生和缺少 API 的环境中验证渲染、导航、缩放、搜索、关闭/重开、无效输入和外部请求拒绝。独立规格与质量审查、完整 Web 与浏览器测试集、仓库契约、受影响认证/前端组件、类型检查、生产构建、资源预算和文档链接通过。报告中的研究 PDF 在缺少桌面 API 的生产策略下可渲染和搜索,完整文档也能在已安装 Electron 运行时解码。
项目人工编辑的二进制发布与历史接口已完成独立规范审查、质量审查和完整后台测试。现有文本写入也使用共享恢复事务;每次写入立即发布,历史按编辑时段分组,恢复新增版本。项目移位后按当前位置恢复,离线时仍能读取已保存版本;失败与损坏记录明确显示未确认状态。
统一预览窗口、文本编辑和后台自动保存控制器已完成源码实现、独立规范与质量审查,以及完整后台、Web、浏览器与文档检查。草稿覆盖未打开的文件,失败不会关闭文件标签;重命名期间禁用相关编辑,成功后按新路径重新打开。人工和模型历史已汇总到同一文件 History,可以预览与恢复。Office 的 DOCX/PPTX/XLSX 已完成真实内容修改、导出与重新打开验证;旧 DOC/PPT/XLS 需要另存 OOXML,ODF 格式已验证原格式导出。保存失败后立即重试,以及关闭前等待保存期间新增内容完成导出,也已在本地引擎中验证。安装后通过默认 App 验证资源与界面,运行状态保存在本地发布记录。
PDF 分页、缩放与搜索,PSD 合成图与图层列表、TIFF 和普通图片预览,以及本地音视频播放已完成源码实现、独立审查和完整验证。预览使用本地固定版本资源,损坏或超限文件显示错误和下载入口;PDF、PSD、TIFF、音频和视频保持只读。
本地 Office 资源的清单校验、独立静态来源和受认证的可用性接口已完成独立审查与完整验证。仅接受固定来源与版本的本地资源,远端来源不可用;编辑器资源不能访问主应用 API。实际 Word、PPT、表格的编辑、导出、预览切换、原生撤销与重开已在研究引擎中验证,保存失败重试及最终导出也已验证。Office 资源独立安装到共享本地缓存;默认 App 不携带资源,安装完成后当前 worker 直接使用已验证版本。字体仅采用附有许可证的输入,资源包保留源码、补丁和来源记录。统一窗口的 Office 预览与编辑接入已完成独立规范、质量审查和完整检查(实现 ef64e6ab):后台 8746 项、Web 单元 672 项、真实 Office 浏览器 16 项和其他预览浏览器 20 项通过,文档 542 页构建通过且无死链。编辑模式等待原生状态确认,原生撤销随标签和历史切换保留,关闭提交尚未完成的输入;旧格式明确转换到新文件。人工/模型历史已完成汇总,
静态 PNG/JPEG/WebP 编辑已完成独立规范、质量审查和完整验证(实现 8dc8bf81)。裁剪、旋转、绘图、形状、文字与原生撤销/重做通过实际导出像素验证;编辑器跨预览、历史、标签和页面切换保留,错误后可重试,转换 PNG 创建独立文件且拒绝覆盖。后台 8746 项、Web 单元 678 项、文件浏览器 30 项和真实 Office 浏览器 16 项通过,文档 542 页构建通过且无死链。首次后台全量运行出现一项并发额度测试超时,未改代码的完整重跑通过。动画与 16 位 PNG 保持只读;限制 64 MiB、1600 万像素和单边 16384 像素。导出不承诺保留嵌入元数据,PSD 仍为合成图和图层预览。
人工和模型文件历史汇总已完成独立规范、质量审查及完整验证(实现 fcf799bd):后台 8767 项、Web 单元 678 项、文件浏览器 32 项、真实 Office 浏览器 16 项通过,文档 542 页构建通过且无死链。首次后台运行的一项取消重试测试超时,候选与任务基线单独复现均通过,未改代码的完整重跑通过。模型版本复用所属会话保留的不可变字节;归档保持可读,彻底删除后不再可读。旧历史有界分批补录,无法核实的归属明确显示不可用。恢复仅修改所选文件并创建新记录;项目移动保持稳定归属,离线只读。主分支整合已通过独立审查和完整组合验证。
最终整合实现 f0e67531 包含当前主分支修复,独立规格与质量复核通过。完整组合验证通过后台 8862 项、Web 单元 678 项、文件窗口浏览器 32 项、真实 Office 浏览器 16 项及 CLI 190 项;Desktop、类型、构建、静态资源体积与 546 页文档检查通过,无死链。前一轮发生自更新测试执行进程退出及替补进程状态复用,聚焦与基线检查通过;保留失败记录,最终完整运行未复现。默认 App 安装与重启验收使用同一整合源码和已验证 Office 资源包,记录与源码检查分开保存。
文件历史持久性已验证:不可变版本、失败重试、损坏记录拒绝、未完成写入投影与旧版本兼容。新增本地生成器继续复用 source_path 发布入口;Office 语义差异和远端条件存储不属于当前实现。
文档 source_path 发布与二进制读取已实现并通过独立审查和测试:完整文件版本随会话持久保存,支持撤回、重新应用及冲突检测;不提供 Office 内容差异视图。
实现证据写入对应测试;本设计页只维护当前合同与总体状态。
跨平台快照一致性
普通文件快照比较读取前后的描述符元数据,以及打开前与读取后的路径元数据。描述符和路径身份的设备与 inode 必须一致。不同操作系统 API 的元数据不能假定具有相同时间戳或权限表示后直接比较。任一观察变化都中止发布;存储权限取自路径观察。在 Windows 上,旧格式迁移先以可写句柄打开暂存文件再刷盘,直到发布成功前始终保留源文件。