Skip to content

Architecture review 02 — Deepen the Editable Flow Diagram consistency module #200

Closed
3 of 3 issues completed

Description

@suntianc

来源:improve-codebase-architecture 架构审查(2026-07-20)。本 issue 作为记录,尚未经过 /grilling 决策树细化。
推荐强度:Strong · 依赖类别:in-process

Files

  • src/main/flow-diagram/flow-diagram-service.ts · 44 symbols
  • src/main/flow-diagram/flow-diagram-revision-store.ts · 24 symbols
  • src/main/flow-diagram/flow-diagram-scene.ts · 21 symbols
  • src/renderer/src/components/FilePanel · 编辑器侧

Problem

最近约 10 个提交都是并发修复(原子替换、compare-and-swap、冲突隔离、reload 协调),每次都跨多个文件——因为没有单一模块拥有一致性规则。一致性不变量在 service、revision-store、scene 与渲染进程之间泄漏。

Solution

一个深的 FlowDiagramStore,在小接口背后拥有 autosave 排序、替换原子性、revision 快照与冲突检测;service 退化为薄 adapter。

Wins

  • locality:一致性 bug 集中在一个模块
  • leverage:一个接口服务 service、tool、renderer
  • 测试直接命中一致性 seam
  • revision-store 与 scene 不再浅薄

ADR 关联

触及 ADR-0071 / ADR-0072(Editable Flow Diagrams 使用本地 Excalidraw 文档 + 隔离 git 历史)。该决策保持不变;本候选加深的是实现它的 模块,而非决策本身。


Spec(/to-spec 调研补充,2026-07-26)

调研结论:问题仍然存在,且比审查时的描述更具体。提交史确认:功能合入(#192-#196)后连续 7 个并发修复提交(原子替换、compare-and-swap、外部变更保留、并发编辑保留、autosave 与导出协调、revision 加固),每个都跨多文件。
新发现的强度不一致:同一份 .excalidraw 文档存在两条互不知晓的写路径——用户编辑器 autosave 走通用文件写入 IPC 的 expectedContent 参数(读-比较-写,非原子,有 TOCTOU 窗口;该参数全库仅此一个消费方),Agent 编辑走 flow-diagram 服务内部的原子 CAS 替换。写排序、冲突检测、reload 协调则由渲染进程编辑器里 6 个 ref + 手写保存队列承担。一致性不变量确实泄漏在 service、revision-store、scene、通用文件服务与渲染进程五处。

Problem Statement

用户与 Agent 会并发编辑同一个 Editable Flow Diagram。保证"谁都不丢改动、失败可回滚、各表面及时刷新"的一致性规则没有唯一归属:每修一个并发 bug 都要同时改 Agent 工具服务、通用文件写入、渲染进程编辑器中的两处或更多,而且两条写路径的原子性强度不同——用户 autosave 路径至今是非原子写。下一个并发 bug 大概率仍要跨文件修,且可能再次只修一条路径。

Solution

建立一个深的 Flow Diagram 文档存储模块(主进程),作为 .excalidraw 文档一致性的唯一拥有者:按文档串行化写入、单一实现的原子 compare-and-swap 替换、写边界的场景校验、Agent 编辑前的 revision 快照与回滚(ADR-0072 隔离 git 历史内化其中)、冲突检测结果与跨表面变更通知。Agent 工具服务退化为薄 adapter(动作解析、澄清、产物展示保留);用户编辑器 autosave 从通用文件写入迁移到该存储背后的专用文档 IPC,冲突时返回当前文档内容,渲染进程只保留展示状态(saved/dirty/saving/error、冲突横幅),删除手写的写排序与 reload 协调。

User Stories

  1. 作为 CDF 用户,我在画布上持续编辑时,autosave 不会覆盖 Agent 刚写入的版本,也不会被 Agent 覆盖——后写的一方基于对方的结果检测冲突。
  2. 作为 CDF 用户,autosave 落盘是原子的:断电、崩溃或写入中断后文档要么是旧版本要么是新版本,绝不半写损坏。
  3. 作为 CDF 用户,当我的编辑与外部变更(Agent 或外部程序改文件)冲突时,编辑器给我明确的冲突提示与当前磁盘内容,我的未保存改动不丢失。
  4. 作为 CDF 用户,Agent 修改现有图前自动记录 revision,Agent 编辑产出无效内容或中途失败时自动恢复原文档(ADR-0072 行为不变)。
  5. 作为 CDF 用户,我表达回滚意图后 Agent 执行 rollback 动作,图恢复到 Agent 编辑前的版本(ADR-0072 行为不变)。
  6. 作为 CDF 用户,Conversation 产物卡、文件面板、编辑器多个表面同时打开同一张图时,任一表面的写入让其他表面及时刷新到最新内容。
  7. 作为 CDF 用户,manage_flow_diagram 工具的全部动作(read_format / create / get / edit / rollback / export)行为与错误码不变,增量编辑继续保留未触及元素与我的布局(ADR-0071 行为不变)。
  8. 作为 CDF 维护者,我想让下一个并发 bug 只需要在一个模块里定位与修复,并且修一次就同时覆盖用户路径与 Agent 路径。
  9. 作为 CDF 维护者,我想删除通用文件写入服务上只为流程图存在的 CAS 参数,让通用文件服务回到简单语义。
  10. 作为 CDF 维护者,我想让渲染进程编辑器收缩为纯展示组件:不再持有写排序队列、磁盘内容镜像与 reload promise 协调。
  11. 作为测试作者,我想直接在存储接口上写并发场景测试(交错写、CAS 失败、中断恢复、快照/回滚),不需要拉起渲染进程或完整 Agent Run。
  12. 作为 CDF 维护者,隔离 git 历史继续不触碰用户仓库的分支、索引、提交与 hooks(ADR-0072 约束不变)。

Implementation Decisions

  • 新模块:Flow Diagram 文档存储,主进程内,唯一拥有:按文档的写串行化、原子 CAS 替换(temp 文件 + rename 语义,单一实现)、写边界场景校验(复用现有场景解析/规范化模块)、revision 快照与回滚(现有 revision store 成为其内部实现)、冲突结果类型、变更通知。
  • Agent 工具服务保留 execute(action) 对外契约(动作集、结果与错误码不变),内部把文档读写、快照、回滚全部委托给存储;服务中现有的原子替换与 CAS 辅助函数迁入存储,不再从服务导出。
  • 编辑器写路径迁移:.excalidraw 文档的读写走新的存储背书的 IPC 通道(读返回内容 + 版本标识;写带版本标识,冲突时返回失败 + 当前内容)。通用文件写入 IPC 的 expectedContent 参数随其唯一消费方一并删除;通用文本编辑器路径不受影响。
  • 渲染进程收缩:编辑器保留 UX 状态机(saved/dirty/saving/error、冲突横幅、Excalidraw 原生 undo/redo),删除手写保存队列、磁盘内容镜像 ref 与外部 reload promise 协调——写排序与冲突判定由主进程存储保证。防抖仍留在渲染进程(纯 UX 节奏)。
  • 导出不动:PNG/SVG 导出的离屏渲染桥保持现状,只从存储读取当前文档。
  • 进程边界:存储位于主进程(文件系统 + git 历史属于高权限能力),渲染进程经 preload 暴露的最小 API 访问,符合 Electron 安全边界。
  • ADR 关联:ADR-0071 / ADR-0072 决策均不变,本票只加深实现它们的模块。

Testing Decisions

  • 好的测试只验证外部行为:给定并发/中断/冲突场景,断言最终磁盘内容、返回的冲突结果、revision 可恢复性、通知触达;不断言内部锁或队列结构。
  • 主测试面是存储接口:真实临时目录 + 真实文件系统,覆盖交错写序、CAS 失败返回当前内容、中断写不损坏文档、Agent 编辑失败自动恢复、rollback 恢复到编辑前 revision。先例:现有 flow-diagram 服务集成测试(temp 目录 + 真实 fs 的写法)。
  • Agent 工具层测试保持现有契约测试(动作→结果/错误码),把一致性场景下沉到存储测试,不重复。
  • 渲染进程编辑器测试聚焦用户可见行为(冲突横幅出现、保存状态指示、不丢未保存改动),沿用 Testing Library 模式。
  • 回归测试先行:迁移前先把 7 个并发修复提交所覆盖的场景在存储接口上重写为测试并确保全绿,再动结构(修 bug 先补失败测试的项目纪律,应用到重构上即为行为锚点)。
  • 验证:pnpm test 覆盖 flow-diagram 全簇 + 文件服务 + 相关渲染组件;涉及 preload/IPC 变更,交付前跑 pnpm run build。

Out of Scope

  • 不改 manage_flow_diagram 工具的动作集、参数与错误码,不新增图表功能。
  • 不改 Excalidraw 文档格式、版本常量与场景规范化规则。
  • 不做图表版本浏览器或手动回滚 UI(ADR-0072 明确不提供)。
  • 不改通用文本编辑器(EditorPane)的保存行为。
  • 不动导出渲染链路。
  • 不迁移其他文件类型到该存储——它只服务 Editable Flow Diagram 文档。

Further Notes

Activity

  1. added
    needs-triageMaintainer needs to evaluate this issue
    architecture架构 deepening 候选(来自架构评审)
    ready-for-agentFully specified, ready for an AFK agent
    and removed
    needs-triageMaintainer needs to evaluate this issue
    on Jul 20, 2026
  2. added
    needs-infoWaiting on reporter for more information
    and removed
    ready-for-agentFully specified, ready for an AFK agent
    on Jul 28, 2026
  3. suntianc commented on Jul 28, 2026

    @suntianc
    OwnerAuthor

    2026-07-28 code-review follow-up:剩余工作拆分与顺序

    当前实现已完成 autosave 专用 IPC、进程内写串行化、原子发布和冲突内容返回,但 review 确认 #200 的完整验收尚未达到。剩余工作按 GitHub 原生子 Issue 拆为:

    关闭本父票前需要三张子票全部完成,并重新执行 flow-diagram 全簇测试、typecheck 与 build。

    由于 #239 存在必须先回答的保证等级问题,本票已从 ready-for-agent 调整为 needs-info。#239 决策落定后,再按依赖顺序把 #240/#241 切换为 ready-for-agent。

  4. suntianc commented on Jul 29, 2026

    @suntianc
    OwnerAuthor

    已在 #245 完成实现并推送;Ubuntu、macOS、Windows CI 均通过。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    architecture架构 deepening 候选(来自架构评审)needs-infoWaiting on reporter for more information

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions