跳到主要内容

ADR-0022:双层一致性(工作区 ↔ 版本 ↔ 实库),禁止自动双向同步

  • 状态:✅ 已接受(2026-08-04;人工拍板主题,Vision 5m 循环按此迭代)
  • 决策者:项目维护者
  • 相关:ADR-0016(体验优先主线)、ADR-0009(presence/增量 sync)、ADR-0008(数据源隔离)

背景​

产品同时存在三个「真相」:内存中的 projectJSON(正在编辑)、已保存的版本(db_change)、以及活的数据库 schema。此前 UI 把它们混在一起表达:

  • 顶栏只有「已保存 / 保存中 / 保存失败」,讲的是落库,不回答「与最新版本比是否有未提交改动」
  • 实库一致性靠 db_version(人写的版本号字符串)比较,compareStringVersion 对空段/非法输入返回 NaN,不可判时表现为「一致」
  • 版本基线取自分页列表的 versions[0],翻页/排序变化即基线漂移
  • 卸载时 closeSocket 无条件 Save.saveProject,结果被丢弃 → 静默覆盖 + 静默失败

用户心智已被 Git 训练过:status 看有没有改动、pull 拿别人的、push 推自己的。产品应借用它,而不是发明新词。

决策​

议题决策
分层A 层(工作区):内存 projectJSON ↔ 最新版本;B 层(实库):模型 ↔ 活库 schema。两层分开呈现,不合并成一个「同步状态」
A 层呈现实时 dirty chip:干净 / 有改动(含变更计数)/ 落库失败;语义与顶栏保存状态合并,不制造重复反馈
A 层基线独立查询最新版本(专用接口或 size:1 排序查询),禁止用分页列表的 versions[0]
B 层呈现五态:一致 / 模型领先 / 库领先 / 双向分叉 / 未知;每态给可行动文案,未知态给 4 路出口
B 层判据实测 schema 指纹(表/列/索引规范化后哈希);db_version 降级为提示,不再作为一致性真相
B 层触发设计器顶栏进页可自动探一次(水合 DB 胶囊);版本页/其它入口仍可显式重探。禁止定时轮询与自动双向同步(拉/推仍须用户发起)
动作隐喻「拉取」= 从实库反向解析 → 保存为版本;「推送」= 同步 DDL → 成功后钉基线。二者都是用户显式动作
红线禁止自动双向同步(无论定时、进页面、还是「顺手帮用户」)。模型与实库之间的写操作必须由用户发起并可预览
不可判即未知版本号/指纹无法比较时必须显示「未知」,禁止默认「一致」
分享访客只读分享不暴露 B 层(无凭证、不代表访客探库)
并发project 保存走乐观锁(冲突 → 可行动提示,不静默覆盖);db_change.version 加唯一约束(Flyway);卸载不做盲存,仅脏时落库且结果可见
北极星口径「有版本保存」= 非空 diff 的版本;空 changes 的保存不计入指标

后果​

  • 正面:状态不再骗人(未知就是未知);并发下不丢数据;用户用 Git 习惯即可理解;实库操作永远由人拍板
  • 代价:多一次「最新版本」查询;schema 指纹需按方言规范化(首期 P0 四库),实现量大于比字符串;顶栏进页自动探一次以水合状态(仍禁止定时双向同步)
  • 明确不做:定时/自动的双向同步;把实库状态塞进 projectJSON;用 db_version 继续当一致性真相;替访客探库