Docmost 第二批：不可变快照与评审发布
完成日期：2026-10-18；承接第一批 bf18b5a42c981d28f4b5c4b808f31a94d30284e7。
本批完成 D03–D06；D07 门户案例、应用内对应源码入口及公网部署仍待后续。

最终行为
提交时等待服务器确认标题和正文已持久化，事务内锁定源页面并校验期望修订，再复制不可变快照。
指定的其他编辑成员批准或带原因退回；每个申请只有一个最终决定。发布只引用该申请的已批准快照。
提交 A 后继续编辑成 B，批准和发布仍对应 A。发布 B 必须重新评审；发布不覆盖草稿，不开启公网分享。
页面操作菜单新增“评审与发布”，提供评审人选择、静态预览、理由、版本差异与发布历史；390px窄屏可阅读。

本次贡献
1. 新增5张表：review_snapshots、document_reviews、review_decisions、document_releases、review_action_requests。
2. 新增7个受认证的POST接口：/api/document-reviews/{revision,reviewers,list,info,submit,decision,publish}。
3. 标题和正文规范JSON的SHA256修订比对；快照保存标题、JSON、纯文本、正文摘要、来源修订及空间。
4. PostgreSQL禁止快照UPDATE；使用事务、行锁和状态version控制并发。同请求键重放原结果，输入不同返回409。
5. 每次操作按当前用户、工作区、空间和页面权限检查；拒绝自批、非指定评审人、撤权后操作和跨工作区访问。
6. 软删除或移动空间后不再访问旧快照；硬删除清理业务记录，保留无正文的请求墓碑，防止旧键误重建。
7. 前端将候选绑定当前协作会话和页面，核验数据库确认；取消、换页、断线或内容变化阻止继续提交。
8. 修复真实数据库驱动的JSON双重编码和嵌套键转换问题，保证快照正文及Unicode原文不被改写。

保存与显示边界
Yjs同步或WebSocket确认不等于数据库已写入。新增流程通过revision接口与提交事务二次校验处理此边界。
只读预览使用固化JSON，不执行动态转引或外部媒体；附件显示已保存引用，不生成匿名附件直链。
网络响应丢失不虚报成功或取消，保留同一请求标识供重试；关闭后可重新打开查看服务端真实状态。
快照UPDATE由数据库阻止；这不是对数据库管理员的防篡改系统，也没有宣称内容永久保留。
现有实时编辑器、协作引擎、评论、搜索和基础权限仍为Docmost上游能力。

验证结果
- pnpm build --skip-nx-cache：3项目构建成功，含开源核心边界检查；未使用构建缓存。
- 客户端Vitest：12文件、118项通过。
- 后端相关Jest：12套件、133项通过，包含2项真实PostgreSQL驱动测试，无跳过。
- 第二批真实HTTP/PostgreSQL/Redis/双WebSocket：73项通过；另一次实际重启后2项通过。
- 第一批真实回归：46项通过；首次初始化因已有工作区跳过1项。
- 数据迁移：空库到53条基线迁移，再升级、回退、重应用本批迁移成功；旧哨兵数据保留，临时数据库移除。
- 并发12请求、批准/退回竞争、重复发布、权限撤销、父页限制、组成员、跨工作区、删除/移动及失败回滚通过。
- 两个浏览器身份实际完成提交、退回、修改、再提交、批准、改草稿、发布；重启后仍为批准的A2，草稿为B。
- 只读会话可看快照，没有提交/决定/发布按钮；390px视口无横向溢出，Escape可关闭弹窗。
- 原39页逐行MD5一致；维修原目录844文件及Git状态保持，门户原目录61文件保持。

审查与限制
独立审查修复3类异步竞态：最终摘要期间取消/换页、已发送请求丢响应误报取消、摘要期间编辑或断线。
新增8条回归先失败后通过；真实联调发现的JSON保存问题也保留了失败和修复后的测试证据。
特殊JSON键/Unicode测试在上游页面API拒绝复杂属性后使用独立SQL夹具；它证明持久化边界，不代表UI支持输入这些属性。
第一批确认的4个上游依赖注入脚手架失败仍作为已知限制保留：auth.controller、auth.service、token.service、page.service。
本批验证相关套件，不宣称全库Jest全绿。构建仍有上游弃用及chunk体积警告。
权限即时重查用于新增delivery/document-review流程，其他上游接口默认缓存策略不在本批改造范围。
只验证Node服务加专用PostgreSQL/Redis容器路径；未构建完整Docker应用镜像。仅本机演示，未公网部署。

保留LICENSE、上游和第三方声明；源码归档随验收交付。上游版本、SHA与排除目录见upstream.json。
运行见run-local.txt；详细设计、HTTP输入输出和任务边界见batch02-plan.txt。
