# New Editor Win32 窗口架构重构总计划 ## 1. 文档目的 本计划用于指导 `new_editor/app/Platform/Win32` 相关窗口系统的彻底重构。 这不是一次“把大类再拆小一点”的整理,而是一次底层架构重建。核心目标是把当前体系改造成: **状态机 + 单向事件流 + 平台投影** 本计划覆盖: - `new_editor/app/Platform/Win32/Windowing` - `new_editor/app/Platform/Win32/Runtime` - `new_editor/app/Platform/Win32/Chrome` - `new_editor/app/Platform/Win32/Content` - `new_editor/app/Platform/Win32/System` - `new_editor/app/Composition` 中与窗口、workspace、utility window、全局上下文相关的部分 --- ## 2. 当前系统的根因 ### 2.1 根因结论 当前最根本的问题只有一个: **`EditorWindow` 没有形成真正的状态边界,窗口相关状态被多个类共同持有、共同修改、相互穿透。** 这带来的直接后果是: 1. 单窗口状态没有唯一所有者。 2. 跨窗口状态没有唯一所有者。 3. `WndProc` 路径承担了过多业务编排职责。 4. 稳态渲染与即时渲染双轨并存,且存在重入。 5. transfer request、binding 探测、兼容状态机大量出现,本质上都在给“边界失效”打补丁。 ### 2.2 当前系统的派生问题 #### 2.2.1 `EditorWindow` 变成了共享可变状态包 它同时挂着: - lifecycle - HWND / title / dpi / size - input controller - chrome controller - runtime controller - frame orchestrator - queued transfer requests 但这些状态不由它自己独占修改,而是被 `MessageDispatcher`、`ChromeController`、`HostRuntime`、`LifecycleCoordinator`、`WorkspaceCoordinator` 等对象直接穿透读写。 #### 2.2.2 Win32 消息分发承担了应用层职责 当前 `WM_*` 消息路径中,除了输入翻译,还直接做了: - 生命周期推进 - resize / dpi 响应 - 立即帧渲染 - global tab drag 编排 - workspace 事务提交 - utility window 打开 - chrome 命令执行 这说明当前平台层已经不是适配层,而是业务主链的一部分。 #### 2.2.3 渲染链路是双轨的 现状存在两条渲染主链: - 稳态帧:主循环统一 `RenderAllWindows` - 即时帧:消息回调里直接 render / present 这会造成: 1. 消息回调重入渲染。 2. transfer request 需要额外缓冲。 3. resize / dpi / paint 顺序变得脆弱。 #### 2.2.4 `WorkspaceCoordinator` 已经偷偷变成状态源 当前 `EditorWindowWorkspaceCoordinator` 不只是协调器,而是同时承担: - window set 构造 - workspace 同步 - 快照回滚 - 新窗口创建 - 窗口关闭编排 - global tab drag 会话 - 标题刷新 但它并没有被正式定义为“权威状态源”,所以职责和边界都不稳定。 #### 2.2.5 全局 `EditorContext` 混入了窗口局部能力 当前每个窗口 runtime 都会把自己的局部能力挂到全局 context 上,例如: - text measurer - utility window request 消费 - capture 状态文本依赖 这让全局上下文和局部窗口能力耦在了一起。 ### 2.3 代码层证据 下面这些不是推测,而是当前代码直接暴露出来的结构事实: 1. `new_editor/app/Platform/Win32/Windowing/EditorWindowState.h` 中所谓 `EditorWindowState` 实际只保存了 `HWND`、`windowId`、`title`、`primary`、`lifecycle`。输入、chrome、resize、dpi、capture、frame、content 相关真实状态都不在这个“状态对象”里,这说明系统从命名层面就在伪装一个并不存在的统一状态边界。 2. `new_editor/app/Platform/Win32/Windowing/EditorWindow.h` 通过 `friend class` 把 `EditorWindowChromeController`、`EditorWindowFrameDriver`、`EditorWindowHostRuntime`、`EditorWindowMessageDispatcher`、`EditorWindowLifecycleCoordinator`、`EditorWindowWorkspaceCoordinator` 全部放进 `EditorWindow` 内部。这意味着窗口对象没有封装,只是一个公开状态袋。 3. `new_editor/app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.cpp` 在 `WM_DPICHANGED`、`WM_EXITSIZEMOVE`、`WM_SIZE`、`WM_PAINT` 中直接触发 `RenderAndHandleWindowFrame` 或 `OnPaintMessage`。也就是说 Win32 消息回调直接参与渲染、事务完成和跨模块编排。 4. `new_editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp` 中的 `RenderAllWindows` 不只是“遍历窗口”,它同时承担帧驱动、presentation 刷新、transfer request 聚合和后续分发。平台宿主已经兼任应用层调度器。 5. `new_editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.cpp` 通过 `BuildLiveWindowWorkspaceSet(...)` 从 live windows 反推当前窗口集合,再基于这个集合做同步、提交和回滚。这说明跨窗口 topology 并没有显式权威状态,只能从现场对象拼出来。 6. `new_editor/app/Platform/Win32/Content/EditorWindowContentController.h` 暴露 `TryGetWorkspaceBinding`、`TryGetDockHostBinding`、`TryGetInputFeedbackBinding`、`TryGetTitleBarBinding`。应用层必须去“探测内容层支持哪些能力”,再临时决定走哪条逻辑分支,本质上是拿 capability probing 代替正式协议。 7. `new_editor/app/Platform/Win32/Runtime/EditorWindowRuntimeController.h` 同时持有 D3D12 renderer、texture host、text system、window render loop、screenshot controller、content controller、frame timing、dpi scale。render host、content host、diagnostic host 被揉成了一个类。 8. `new_editor/app/Platform/Win32/Content/EditorWorkspaceWindowContentController.cpp` 里 workspace 内容控制器一边对外暴露 binding,一边直接驱动 shell runtime 和 frame orchestrator。说明“内容输出”不是一个结构化结果,而是一堆散落接口和副作用。 因此,当前问题不是“某几个类太大”,而是根本没有形成: - 单窗口状态所有权 - 跨窗口拓扑状态所有权 - 事件到 effect 的正式管线 - 平台层与应用层的稳定边界 --- ## 3. 重构目标 ### 3.1 总目标 建立以下新的权责模型: 1. **单窗口状态** 只能由 `WindowSession` 持有。 2. **跨窗口拓扑状态** 只能由 `WindowTopologyService` 持有。 3. **平台对象** 由 Win32 / D3D12 backend 持有,但只作为投影结果,不作为业务状态源。 4. **内容层** 不再通过 binding 探测链暴露能力,而是统一返回 `ContentOutput`。 5. **帧调度** 统一由 `FrameScheduler` 管理,消息回调只登记帧请求,不直接渲染。 ### 3.2 完成后必须满足的硬约束 1. 新窗口架构中禁止 `friend` 穿透状态。 2. domain / application 头文件中禁止出现 `HWND`、`WPARAM`、`LPARAM`、`RECT`、`POINT`。 3. `WndProc` 路径中禁止直接 render / present。 4. `EditorWindowFrameTransferRequests` 必须删除。 5. `TryGetWorkspaceBinding` / `TryGetDockHostBinding` / `TryGetInputFeedbackBinding` / `TryGetTitleBarBinding` 必须退出主干架构。 6. 跨窗口状态必须显式保存在 `WindowTopologyState` 中,禁止从 live windows 反推。 7. `EditorContext` 中禁止挂载窗口私有能力对象。 --- ## 4. 目标架构 ## 4.1 分层模型 ```text Domain 状态、事件、命令、意图、effect 定义 Application WindowSession / WindowTopologyService / FrameScheduler / Projector Content WorkspaceContentHost / UtilityContentHost Platform Win32 事件适配、native window backend、D3D12 render host ``` ## 4.2 状态所有权 ### 单窗口状态:`WindowState` 由 `WindowSession` 独占持有,建议至少包含: - identity - `windowId` - `windowKind` - lifecycle - `PendingCreate` - `NativeAttached` - `Initializing` - `Running` - `Closing` - `Destroyed` - presentation - `title` - `dpi` - `dpiScale` - `focused` - `visible` - `minimized` - size - `clientPixelWidth` - `clientPixelHeight` - `predictedClientPixelWidth` - `predictedClientPixelHeight` - input - `pointerCaptureOwner` - `trackingMouseLeave` - `modifierSnapshot` - `pendingEvents` - `pendingDoubleClickMask` - chrome - `hoveredChromeTarget` - `pressedChromeTarget` - `hoveredResizeEdge` - `activeResizeEdge` - `maximized` - `restoreRect` - `dragRestoreState` - frame - `needsFrame` - `framePriority` - `frameSerial` - `resizeEpoch` - content - `cursorKind` - `captureDemand` - `titleBarMode` - `contentStatus` ### 跨窗口状态:`WindowTopologyState` 由 `WindowTopologyService` 独占持有,至少包含: - `primaryWindowId` - `activeWindowId` - `orderedWindowIds` - `workspaceWindowIds` - `utilityWindowIds` - `windowProjections` - `workspaceProjectionSnapshots` - `workspaceSessionSnapshots` - `globalTabDragState` - `pendingUtilityRequests` - `pendingDetachTransactions` - `pendingDropTransactions` 说明: - `WindowTopologyState` 不要求吞掉整个 workspace runtime 对象,但必须显式持有“窗口到 workspace 的权威投影真相”。 - 至少要能按 `windowId` 解释:window kind / role、workspace projection snapshot、workspace session snapshot、utility window kind、primary / active 标记、正在进行的 detach / drop / reuse 事务。 - 禁止再通过 live window、content controller、workspace controller 临时拼装当前业务 window set。 ### 平台状态 平台状态由 platform backend 持有,但不作为业务真状态: - `HWND` - Win32 class registration - OS capture / cursor - D3D12 device / swapchain / renderer --- ## 5. 核心抽象 ## 5.1 Domain 抽象 建议新增目录: ```text new_editor/app/Windowing/Domain/ ``` 建议新增类型: - `WindowId.h` - `WindowState.h` - `WindowTopologyState.h` - `WindowEvent.h` - `WindowIntent.h` - `WindowCommand.h` - `WindowEffect.h` - `WindowCursorKind.h` - `WindowCaptureOwner.h` - `FramePriority.h` ### `WindowEvent` 平台层只负责把 `WM_*` 转成这些事件: - 生命周期 - `NativeAttached` - `NativeDestroyed` - `CloseRequested` - 输入 - `PointerMoved` - `PointerLeft` - `PointerButtonDown` - `PointerButtonUp` - `PointerWheel` - `KeyDown` - `KeyUp` - `Character` - `FocusGained` - `FocusLost` - `CaptureChanged` - 尺寸 - `ClientResized` - `InteractiveResizeStarted` - `InteractiveResizeEnded` - `DpiChanged` - 帧 - `FrameTick` - `PaintRequested` - `FramePresented` - 内容 - `ContentOutputReady` ### `WindowIntent` 内容层不再返回 transfer request,而是返回正式意图: - `StartGlobalTabDrag` - `DetachPanel` - `OpenUtilityWindow` - `RequestCursor` - `RequestCapture` - `ReleaseCapture` - `RequestImmediateFrame` - `RequestInvalidate` - `ProposeWorkspaceMutation` ### `WindowEffect` 应用层最终向平台层发 effect: - `CreateNativeWindow` - `DestroyNativeWindow` - `ShowWindow` - `FocusWindow` - `MoveWindow` - `ResizeWindow` - `SetWindowTitle` - `SetCursor` - `AcquireCapture` - `ReleaseCapture` - `TrackMouseLeave` - `ScheduleFrame` - `ApplyRenderResize` --- ## 6. 目标模块设计 ## 6.1 `WindowSession` 职责: - 持有 `WindowState` - 接收 `WindowEvent` - 演进单窗口状态 - 触发内容更新 - 生成窗口级命令和 effect 禁止: - 直接调用 Win32 API - 直接调用 D3D12 present - 直接修改其它窗口状态 - 直接创建 utility window ## 6.2 `WindowTopologyService` 职责: - 持有 `WindowTopologyState` - 处理跨窗口事务 - 统一管理: - primary / active window - panel detach - cross-window drop - global tab drag - utility window 去重与重用 它必须替代: - `EditorWindowWorkspaceCoordinator` - `EditorUtilityWindowCoordinator` ## 6.3 `FrameScheduler` 职责: - 统一登记所有窗口帧请求 - 合并稳态帧与优先帧 - 对 resize / dpi / paint 恢复设定显式优先级 关键规则: 1. 消息回调只登记帧请求。 2. 所有 render / present 集中在调度点执行。 3. 不允许出现新的“隐式即时帧”路径。 ## 6.4 `IWindowContentHost` 内容层改成统一宿主接口,而不是多 binding 探测。 输入:`WindowContentInput` - workspace bounds - input events - frame context - capture status text - topology snapshot - window role 输出:`WindowContentOutput` - draw data - cursor kind - capture demand - title bar mode - workspace mutation intent - utility window intent - focus hints - status text ## 6.5 `WorkspaceContentHost` 替代: - `EditorWorkspaceWindowContentController` 职责: - 持有 `EditorShellRuntime` - 更新 shell - 输出统一 `WindowContentOutput` ## 6.6 `UtilityContentHost` 替代: - `EditorUtilityWindowContentController` 职责: - 承载 utility panel - 输出聚焦态、绘制结果、最小尺寸和内容状态 ## 6.7 `Win32WindowProcAdapter` 替代: - `EditorWindowMessageDispatcher` 职责: - `WM_* -> WindowEvent` - 不做业务决策 - 不持有业务状态 - 不 render / present ## 6.8 `Win32WindowBackend` 职责: - `CreateWindowExW` - `DestroyWindow` - `SetWindowPos` - `SetWindowTextW` - `ShowWindow` - `SetForegroundWindow` - `TrackMouseEvent` - `SetCapture / ReleaseCapture` 原则: - 只执行 effect - 不知道 workspace - 不知道 topology - 不知道 content host ## 6.9 `D3D12WindowRenderHost` 替代旧 `EditorWindowRuntimeController` 中的 D3D12 部分。 职责: - 初始化窗口 renderer - resize - begin frame - present - viewport surface presentation capability 它是渲染宿主,不是业务控制器。 ## 6.10 Chrome 子层 当前 stateful `EditorWindowChromeController` 需要拆为: - `ChromeGeometry` - 纯几何、纯 hit test - `ChromeReducer` - hover / press / resize / drag restore 状态机 - `ChromeEffectsBuilder` - 根据状态变化生成 effect 纯 helper 如 `BorderlessWindowChrome.*`、`BorderlessWindowFrame.*` 可以保留或收缩。 ## 6.11 关键接口草案 下面给的是目标结构草案,不要求首阶段一次到位,但最终主干必须收敛到类似形态。 ### `WindowSession` ```cpp class WindowSession final { public: explicit WindowSession(WindowId id, WindowKind kind); const WindowState& Snapshot() const; WindowSessionResult ApplyEvent(const WindowEvent& event); WindowSessionResult ApplyContentOutput(const WindowContentOutput& output); WindowSessionResult ApplyTopologyFeedback(const WindowTopologyFeedback& feedback); private: WindowState m_state = {}; }; ``` 约束: - `WindowSession` 是单窗口真状态唯一可写者。 - 外部只能拿 snapshot,不能直接改字段。 - chrome、input、lifecycle、frame 都通过 reducer 更新,不允许 controller 私改。 ### `WindowTopologyService` ```cpp class WindowTopologyService final { public: const WindowTopologyState& Snapshot() const; TopologyResult ApplyIntent( WindowId sourceWindowId, const WindowIntent& intent, const WindowTopologySnapshot& snapshot); void OnWindowClosed(WindowId windowId); void OnWindowCreated(WindowId windowId, WindowRole role); private: WindowTopologyState m_state = {}; }; ``` 约束: - 所有跨窗口事务统一进这里。 - 不允许在别处直接增删 `workspaceWindowIds` / `utilityWindowIds`。 - global tab drag 只能由它维护完整会话状态。 ### `FrameScheduler` ```cpp class FrameScheduler final { public: void RequestFrame(WindowId windowId, FramePriority priority, FrameReason reason); bool HasPendingFrame() const; FrameBatch DequeueReadyBatch(const WindowSessionStore& sessions); void NotifyFramePresented(WindowId windowId, std::uint64_t frameSerial); }; ``` 约束: - 只有 `FrameScheduler` 可以决定某一帧何时真正执行。 - `WM_PAINT`、`WM_SIZE`、`WM_DPICHANGED` 只产生请求,不直接 render。 - steady frame / immediate frame 不再是两条链,而是一个调度器内的不同优先级。 ### `FrameScheduler` 帧事务模型 单帧事务顺序必须固定,不允许实现时自由发挥。标准顺序定义为: 1. drain window events 2. merge frame requests 3. build `WindowContentInput` 4. update content hosts 5. submit topology intents / effect intents 6. finalize render plan 7. execute render subpasses / viewport work 8. present 9. publish `FramePresented` / frame feedback 10. validate paint / clear frame debt / decide requeue 必须同时定义以下语义: - `WM_PAINT` 只产生 `FrameReason::PaintValidation` - `WM_SIZE` 只产生 `FrameReason::Resize` - `WM_DPICHANGED` 只产生 `FrameReason::DpiChange` - steady tick 只产生 `FrameReason::SteadyTick` - global tab drag / capture 恢复只产生显式 interaction continuation reason 优先级规则必须显式写死: - `DpiChange` = `Critical` - `Resize` = `High` - `PaintValidation` = `High` - `InteractionContinuation` = `High` - `SteadyTick` = `Normal` 旧语义替代关系必须明确: - `DriveFrame` -> steady tick frame request - `DriveImmediateFrame` -> high/critical priority frame request - `RequestSkipNextSteadyStateFrame` -> scheduler suppression rule - queued completed immediate frame -> scheduler-owned post-present topology transaction queue ### `IWindowContentHost` ```cpp class IWindowContentHost { public: virtual ~IWindowContentHost() = default; virtual void Initialize(const WindowContentHostInitContext&) = 0; virtual WindowContentOutput Update(const WindowContentInput&) = 0; virtual void RenderSubpasses(const WindowRenderSubpassContext&) = 0; virtual void Shutdown() = 0; }; ``` 约束: - 内容层统一返回 `WindowContentOutput`。 - 应用层不再 `TryGet*Binding`。 - 内容层如果需要表达 cursor、capture、detach、utility window、title bar mode,都在 output 中显式返回。 ### `Win32WindowProcAdapter` ```cpp class Win32WindowProcAdapter final { public: std::optional Translate( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) const; }; ``` 约束: - 只做消息翻译,不做业务分支。 - 不调用 workspace coordinator。 - 不调用 utility coordinator。 - 不调用 render / present。 --- ## 7. 旧模块到新模块的映射 | 现有模块 | 目标归宿 | 最终命运 | | --- | --- | --- | | `EditorWindow` | `WindowSession` + 轻量 native facade | 大幅收缩或删除 | | `EditorWindowManager` | `WindowSessionStore` + `WindowTopologyService` + `FrameScheduler` | 删除 | | `EditorWindowHostRuntime` | `WindowSessionStore` + `Win32WindowBackend` | 删除 | | `EditorWindowMessageDispatcher` | `Win32WindowProcAdapter` | 删除 | | `EditorWindowLifecycleCoordinator` | `WindowSession` + topology service | 删除 | | `EditorWindowWorkspaceCoordinator` | `WindowTopologyService` | 删除 | | `EditorUtilityWindowCoordinator` | `WindowTopologyService` | 删除 | | `EditorWindowFrameDriver` | `FrameScheduler` | 删除 | | `EditorWindowRuntimeController` | `D3D12WindowRenderHost` + content host bridge | 拆分后删除旧形态 | | `EditorWorkspaceWindowContentController` | `WorkspaceContentHost` | 替换 | | `EditorUtilityWindowContentController` | `UtilityContentHost` | 替换 | | `EditorWindowTransferRequests` | `WindowIntent / WindowCommand / WindowEffect` | 删除 | | `TryGet*Binding` 链 | `WindowContentOutput` | 删除 | ## 7.1 最终顶层编排者 最终必须引入新的顶层运行时,建议命名: - `WindowApplicationRuntime` 它的职责只能是: - 持有 `WindowSessionStore` - 持有 `WindowTopologyService` - 持有 `FrameScheduler` - 持有 content host factory - 持有 `Win32WindowBackend` - 持有 `Win32WindowProjector` - 持有 `D3D12WindowRenderHost` registry - 驱动主循环调度 它明确禁止做的事: - 不新增业务可写状态真相 - 不直接执行 Win32 API - 不直接做 workspace 事务 - 不把 topology / session / frame 规则塞回 `Application` `Application` 最终只保留: - 进程级初始化 - crash / log / DPI bootstrap - 创建 `WindowApplicationRuntime` - 调用 runtime run / shutdown --- ## 8. 目录重组建议 建议新增目录: ```text new_editor/app/Windowing/Domain/ new_editor/app/Windowing/Application/ new_editor/app/Windowing/Content/ new_editor/app/Platform/Win32/Adapter/ new_editor/app/Platform/Win32/Backend/ new_editor/app/Platform/Win32/Projection/ new_editor/app/Platform/Win32/Rendering/ ``` 旧目录的处理策略: - `Platform/Win32/Windowing` 先收缩为迁移桥,最终清空或仅保留少量 native glue - `Platform/Win32/Runtime` D3D12 相关迁出后删除旧 driver / controller - `Platform/Win32/Content` 迁入 `Windowing/Content` - `Platform/Win32/Chrome` 仅保留纯 helper,删除 stateful controller ## 8.1 依赖与边界规则 重构过程中必须同时执行“依赖收口”,否则类名换了,旧问题会原样复活。 ### 允许的依赖方向 ```text Domain <- Application <- Content <- Platform Projection Platform Backend 只被 Application/Projection 调用 Rendering Host 只被 FrameScheduler/Render Pipeline 调用 ``` ### 明确禁止的依赖 1. `Windowing/Domain` 禁止包含 `windows.h`、D3D12、swapchain、renderer 头文件。 2. `Windowing/Application` 禁止直接调用 `CreateWindowExW`、`SetWindowPos`、`SetCapture`、`Present`。 3. `Windowing/Content` 禁止感知 `HWND`、`RECT`、`POINT`、`WPARAM`、`LPARAM`。 4. `Platform/Win32/Adapter` 禁止包含 workspace 事务逻辑。 5. `Platform/Win32/Backend` 禁止读取 workspace/session/content 模型。 6. `WindowTopologyService` 之外禁止直接维护 global tab drag 会话。 7. `FrameScheduler` 之外禁止新增任何“临时立即帧”入口。 8. `EditorContext` 之外的全局单例禁止继续扩散;新增全局能力必须先证明不是 per-window。 ### include 审核规则 审核员在代码审查时必须额外检查: - 新建 domain 头文件是否仍然引用 Win32 类型。 - 新建 content host 是否仍然暴露 binding 探测接口。 - 新建 platform 代码是否偷偷包含 `UIEditorWorkspaceController`。 - 任何新类是否再次通过 `friend` 穿透 `WindowSession`。 ## 8.2 迁移期间的反腐层规则 迁移不是一口气删光旧代码,而是分期替换。但桥接层必须受限,否则过渡层会永久化。 ### 允许存在的临时桥 1. `EditorWindowManager` 只允许暂时作为启动入口和 legacy facade。 2. `EditorWindow` 过渡期允许保留,但只能逐步退化为: - `windowId` - native handle facade - 指向 `WindowSession` / backend 的轻量桥 3. `EditorWindowRuntimeController` 只允许短期作为 `content host + render host` 的组合桥,后续必须拆解。 4. `EditorWindowMessageDispatcher` 只允许短期 forward 到 `Win32WindowProcAdapter`,不能继续长逻辑。 所有临时桥都必须同时满足以下硬约束: 1. 只允许做机械转发或数据形状适配。 2. 不允许持有业务可写状态。 3. 不允许做条件分支决策。 4. 不允许生成新的业务语义。 5. 不允许吞掉 effect、intent、错误或反馈。 6. 必须在计划中绑定删除阶段和删除条件。 7. 做不到以上约束的桥,不允许“先留着”,必须在当前阶段直接拆掉。 ### 迁移期明确禁止 1. 禁止新增任何新的 `friend class`。 2. 禁止给 `EditorWindowFrameTransferRequests` 再加字段。 3. 禁止新增新的 `TryGet*Binding`。 4. 禁止在 `WM_*` 路径里新增 render / present / workspace 事务代码。 5. 禁止在旧 coordinator 中继续沉积新业务。 6. 禁止从 live windows 再推导新的“临时全局状态”。 ### 判断一个桥是否可以删除的标准 同时满足以下条件时,桥必须进入删除阶段: 1. 新结构已经有等价状态对象。 2. 新结构已经有等价命令或 effect。 3. 旧桥只剩转发,不再做决策。 4. 已有回归覆盖该路径。 ## 9.0 所有阶段通用门禁 下面这些门禁不是某一阶段独有,而是从 Phase 1 开始到 Phase 8 都必须成立: 1. 本阶段新增桥接层只能是机械转发 adapter,不能业务化。 2. 任何单窗口状态在同一时刻只能有一个可写源。 3. 任何跨窗口状态在同一时刻只能有一个可写源。 4. 新增类型不得把 Win32 类型重新包装后带回 domain / application。 5. 没有对应测试门禁和回归命令的结构,不允许宣称阶段完成。 --- ## 9. 分阶段执行方案 ## Phase 0:基线冻结与护栏建立 ### 目标 在动架构前冻结当前行为基线。 ### 工作项 1. 记录旧系统关键链路: - 建窗 - 关闭 - steady frame - immediate frame - resize - dpi change - utility window open / reuse - panel detach - global tab drag 2. 补最小回归清单。 3. 建立 trace / log 基线。 4. 标记本计划的阶段门禁。 ### 退出条件 - 关键行为已文档化。 - 有最小回归检查项。 - 已定义测试 target 规划、fake 对象职责和阶段门禁命令。 - 评论区已登记 Phase 0 启动记录。 ## Phase 1:新协议落地 ### 目标 先引入新的 domain 抽象,不替换旧主链。 ### 工作项 1. 新增 `WindowState`、`WindowTopologyState`、`WindowEvent`、`WindowIntent`、`WindowEffect`。 2. 新增 `WindowContentInput`、`WindowContentOutput`。 3. 新增纯 reducer 雏形: - lifecycle - input - chrome 4. 保持旧主链不动。 ### 退出条件 - 新类型不依赖 Win32 类型。 - 新类型足以表达旧主链中的关键意图。 - 已把 `window_session_tests`、`window_topology_tests`、`frame_scheduler_tests`、`win32_message_translator_tests` 纳入 CMake 目标规划与目录规划。 ## Phase 2:单窗口状态迁入 `WindowSession` ### 目标 解决最底层的状态 ownership 问题。 ### 工作项 1. 创建 `WindowSession`。 2. 把单窗口状态迁入 `WindowSession`。 3. `EditorWindow` 退化为兼容外壳。 4. 开始消灭外部对 `EditorWindow` 内部状态的直接访问。 ### 退出条件 - 单窗口真状态只在 `WindowSession` 或其 reducer 中可写。 - 旧 `EditorWindow` / `EditorWindowInputController` / `EditorWindowChromeController` 不能再持有独立业务状态真相。 - 不允许存在新旧双写路径。 - `EditorWindow` 不再是共享可变状态包。 ## Phase 3:消息路径改造 ### 目标 把 `WndProc` 路径变成纯事件适配。 ### 工作项 1. 新建 `Win32WindowProcAdapter`。 2. 所有 `WM_*` 转成 `WindowEvent`。 3. 移出消息路径中的: - render / present - workspace 事务 - utility window 打开 - chrome 直接 effect ### 退出条件 - `WndProc` 中不再直接调用渲染入口。 ## Phase 4:统一帧调度 ### 目标 消灭稳态帧与即时帧的双轨结构。 ### 工作项 1. 建立 `FrameScheduler`。 2. 消息路径只登记帧请求。 3. 稳态帧和优先帧统一调度。 4. 定义完整 frame lifecycle、`FrameReason` 和 `FramePriority`。 5. 删除 `DriveImmediateFrame` 思维模式。 ### 退出条件 - 没有消息路径直接 render / present。 - `FrameScheduler` 生命周期顺序、reason 映射、priority 映射、失败/跳帧规则已落成文档并对应旧语义。 - 不再需要 queued completed immediate frame 补偿逻辑。 ## Phase 5:内容层协议替换 ### 目标 删除 binding 探测链。 ### 工作项 1. 建立 `IWindowContentHost`。 2. 实现 `WorkspaceContentHost`。 3. 实现 `UtilityContentHost`。 4. 让内容层统一返回 `WindowContentOutput`。 5. 删除 `TryGet*Binding` 链。 ### 退出条件 - 应用层不再感知内容层内部的 binding 形状。 ## Phase 6:拓扑服务落地 ### 目标 建立显式 `WindowTopologyState`。 ### 工作项 1. 建立 `WindowTopologyService`。 2. 迁移: - primary / active window - panel detach - cross-window drop - global tab drag - utility window 管理 3. 停止从 live windows 反推业务 window set。 ### 退出条件 - 系统里只有一个跨窗口拓扑状态源。 - `WindowTopologyState` 已显式保存 window projection / workspace projection / session snapshot / 事务快照。 - `BuildLiveWindowWorkspaceSet(...)` 不再承担业务真相恢复职责。 ## Phase 7:平台投影与后端收口 ### 目标 把 Win32 / D3D12 执行逻辑从业务决策中彻底剥离。 ### 工作项 1. 新建 `WindowApplicationRuntime`。 1. 新建 `Win32WindowBackend`。 2. 新建 `Win32WindowProjector`。 3. 新建 `D3D12WindowRenderHost`。 4. 把 `Application` 收缩为 bootstrap,主循环调度迁给 `WindowApplicationRuntime`。 5. 所有 Win32 effect 都通过 backend 执行。 ### 退出条件 - `EditorWindowManager` 删除后的顶层调用链已经明确落到 `WindowApplicationRuntime`。 - 应用层不再直接调用 Win32 API。 - 会话层不再直接持有 D3D12 业务控制职责。 ## Phase 8:删除旧结构与最终清理 ### 目标 删掉所有旧桥、旧接口、旧控制器。 ### 工作项 1. 删除旧 coordinator / driver / dispatcher / transfer request。 2. 删除旧 binding 探测链。 3. 收缩 `EditorContext`。 4. 清理 include 方向和目录结构。 ### 退出条件 - 本计划第 3 节的硬约束全部满足。 ## 9.1 文件级实施矩阵 这一节是执行时的真正落地清单。阶段说明解决“做什么”,这一节解决“先动哪些文件、怎么桥接、删哪些旧件”。 ### Phase 0 文件级动作 目标:冻结现状,不改行为。 重点读取与建档文件: - `new_editor/app/Platform/Win32/Windowing/EditorWindow.h/.cpp` - `new_editor/app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.cpp` - `new_editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp` - `new_editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.cpp` - `new_editor/app/Platform/Win32/Windowing/EditorUtilityWindowCoordinator.cpp` - `new_editor/app/Platform/Win32/Runtime/EditorWindowFrameDriver.cpp` - `new_editor/app/Platform/Win32/Runtime/EditorWindowRuntimeController.h/.cpp` - `new_editor/app/Platform/Win32/Chrome/EditorWindowChromeController.h/.cpp` 必须沉淀的基线: - 消息到渲染路径时序图 - steady frame 时序图 - immediate frame 时序图 - global tab drag 时序图 - detach panel -> 新窗口创建时序图 - utility window open / reuse 时序图 审核检查点: - 文档是否覆盖所有 `WM_*` 关键路径 - 是否识别了所有 direct Win32 API 调用点 - 是否列出当前 transfer request 生产者和消费者 ### Phase 1 文件级动作 目标:先落地新协议,不改主行为。 新增文件建议: - `new_editor/app/Windowing/Domain/WindowId.h` - `new_editor/app/Windowing/Domain/WindowKind.h` - `new_editor/app/Windowing/Domain/WindowState.h` - `new_editor/app/Windowing/Domain/WindowTopologyState.h` - `new_editor/app/Windowing/Domain/WindowEvent.h` - `new_editor/app/Windowing/Domain/WindowIntent.h` - `new_editor/app/Windowing/Domain/WindowCommand.h` - `new_editor/app/Windowing/Domain/WindowEffect.h` - `new_editor/app/Windowing/Domain/WindowContentInput.h` - `new_editor/app/Windowing/Domain/WindowContentOutput.h` - `new_editor/app/Windowing/Domain/FramePriority.h` 新增 reducer 建议: - `new_editor/app/Windowing/Application/Reducers/WindowLifecycleReducer.*` - `new_editor/app/Windowing/Application/Reducers/WindowInputReducer.*` - `new_editor/app/Windowing/Application/Reducers/WindowChromeReducer.*` 桥接策略: - 旧 `EditorWindow` 继续跑,但开始把内部可见状态映射到新的 `WindowState` 草案。 - 新 domain 类型只作为纯类型定义,不接管现网行为。 本阶段不能做的事: - 不能让新 domain 类型依赖 `windows.h` - 不能在新协议里出现 `HWND` - 不能为了兼容旧代码把 Win32 类型偷偷包一层再塞回 domain ### Phase 2 文件级动作 目标:把单窗口真状态收回到 `WindowSession`。 新增文件建议: - `new_editor/app/Windowing/Application/WindowSession.h` - `new_editor/app/Windowing/Application/WindowSession.cpp` - `new_editor/app/Windowing/Application/WindowSessionStore.h` - `new_editor/app/Windowing/Application/WindowSessionStore.cpp` - `new_editor/app/Windowing/Application/WindowSessionResult.h` 优先迁移状态来源: 1. `EditorWindowState.h` 2. `EditorWindowInputController` 3. `EditorWindowChromeController` 4. `EditorWindow` 中与 resize / dpi / queued immediate frame 相关的状态 具体做法: - 先把当前 scattered state 建成 `WindowState` 快照字段,不立即删旧控制器。 - 让 `EditorWindow` 只通过 `WindowSession` 读写窗口状态。 - 外部模块改成读取 `WindowSession::Snapshot()`,不再读 `EditorWindow` 私有成员。 - 旧 controller 如果暂时还存在,只允许退化为无状态 helper,不允许继续做独立状态持有者。 必须优先清掉的穿透点: - `EditorWindow.h` 中的 `friend class` 链 - `EditorWindowMessageDispatcher` 对 `m_inputController` / `m_chromeController` / `m_runtime` 的直接访问 - `EditorWindowFrameDriver` 对 `window.m_chromeController` 的直接访问 阶段验收: - 所有单窗口状态写入都只经由 `WindowSession` 或 reducer - `EditorWindow` 不再是唯一可写状态袋 - 不存在旧 controller 与 `WindowSession` 双写 - `friend class` 不再承担状态穿透写入入口 ### Phase 3 文件级动作 目标:把消息路径收口成纯适配层。 新增文件建议: - `new_editor/app/Platform/Win32/Adapter/Win32WindowProcAdapter.h` - `new_editor/app/Platform/Win32/Adapter/Win32WindowProcAdapter.cpp` - `new_editor/app/Platform/Win32/Adapter/Win32MessageTranslator.*` 旧文件处理: - `EditorWindowMessageDispatcher.cpp` 第一阶段变薄,只保留: - `HWND -> WindowId` 查找 - 调用 `Win32WindowProcAdapter` - 把事件送给 `WindowSession` - 后续删除 `EditorWindowMessageDispatcher.*` 需要迁出的逻辑: - `WM_SIZE` 内直接 immediate frame - `WM_DPICHANGED` 内直接 render - `WM_PAINT` 内直接 `OnPaintMessage` - `WM_CLOSE` 中对生命周期的复杂编排 - `WM_MOUSE*` 中直接 global tab drag / chrome action 决策 阶段验收: - `WndProc` 中只剩翻译和转发 - 业务层收到的是 `WindowEvent`,不是 `WM_*` ### Phase 4 文件级动作 目标:建立统一帧调度器。 新增文件建议: - `new_editor/app/Windowing/Application/FrameScheduler.h` - `new_editor/app/Windowing/Application/FrameScheduler.cpp` - `new_editor/app/Windowing/Application/FrameRequestQueue.h` - `new_editor/app/Windowing/Application/FrameRequestQueue.cpp` 旧文件收缩: - `Runtime/EditorWindowFrameDriver.*` 先转成 scheduler 内部 helper,再删除 - `EditorWindowHostRuntime::RenderAllWindows(...)` 改为委托 `FrameScheduler` 关键迁移动作: - 把 `DriveFrame` / `DriveImmediateFrame` 收束成统一请求模型 - 把 `RequestSkipNextSteadyStateFrame` 这种补丁语义转为显式调度规则 - 把 `HasQueuedCompletedImmediateFrame` / `ConsumeQueuedCompletedImmediateFrameTransferRequests` 转换为 scheduler 完成回调或拓扑事务队列 - 把 frame transaction 顺序固定到: event drain -> request merge -> content update -> topology submit -> render -> present -> feedback -> paint validation - 为 `WM_PAINT` / `WM_SIZE` / `WM_DPICHANGED` 建立明确的 `FrameReason` / `FramePriority` 映射 阶段验收: - steady frame / immediate frame 共享同一套调度实体 - 新代码里不存在“为了某个消息直接 present 一帧”的入口 ### Phase 5 文件级动作 目标:把内容层从 binding 探测链改成正式输出协议。 新增文件建议: - `new_editor/app/Windowing/Content/IWindowContentHost.h` - `new_editor/app/Windowing/Content/WorkspaceContentHost.h` - `new_editor/app/Windowing/Content/WorkspaceContentHost.cpp` - `new_editor/app/Windowing/Content/UtilityContentHost.h` - `new_editor/app/Windowing/Content/UtilityContentHost.cpp` 重点改造旧文件: - `Platform/Win32/Content/EditorWindowContentController.h` - `Platform/Win32/Content/EditorWorkspaceWindowContentController.*` - `Platform/Win32/Content/EditorUtilityWindowContentController.*` 替换策略: 1. 先让旧 content controller 内部构造 `WindowContentOutput` 2. 再让应用层只消费 output,不再 `TryGet*Binding` 3. 等应用层不再 probe binding 后,删除 binding 接口 必须退出主干的接口: - `TryGetWorkspaceBinding` - `TryGetDockHostBinding` - `TryGetInputFeedbackBinding` - `TryGetTitleBarBinding` - `EditorWindowFrameTransferRequests` 阶段验收: - 内容层对外只暴露一个宿主接口 - cursor / capture / title / utility / detach 等信号都能通过 output 表达 ### Phase 6 文件级动作 目标:建立显式拓扑状态源。 新增文件建议: - `new_editor/app/Windowing/Application/WindowTopologyService.h` - `new_editor/app/Windowing/Application/WindowTopologyService.cpp` - `new_editor/app/Windowing/Application/WindowTopologyResult.h` - `new_editor/app/Windowing/Application/WindowTopologySnapshot.h` 优先迁移旧文件职责: - `Windowing/EditorWindowWorkspaceCoordinator.*` - `Windowing/EditorUtilityWindowCoordinator.*` 必须先抽离的事务: - active / primary window 管理 - global tab drag 会话 - panel detach -> 新窗口创建 - cross-window drop - utility window open / reuse / focus 核心要求: - `BuildLiveWindowWorkspaceSet(...)` 只能作为短期兼容代码,最终删除 - topology state 必须显式保存,而不是运行时临时拼装 - topology state 必须持有按 `windowId` 建立的权威 workspace projection / session snapshot 阶段验收: - 任何跨窗口行为都能从 `WindowTopologyState` 解释 - 不再需要从 live windows 反推出唯一真相 ### Phase 7 文件级动作 目标:把平台执行层和业务决策彻底分开。 新增文件建议: - `new_editor/app/Windowing/Application/WindowApplicationRuntime.h` - `new_editor/app/Windowing/Application/WindowApplicationRuntime.cpp` - `new_editor/app/Platform/Win32/Backend/Win32WindowBackend.h` - `new_editor/app/Platform/Win32/Backend/Win32WindowBackend.cpp` - `new_editor/app/Platform/Win32/Projection/Win32WindowProjector.h` - `new_editor/app/Platform/Win32/Projection/Win32WindowProjector.cpp` - `new_editor/app/Platform/Win32/Rendering/D3D12WindowRenderHost.h` - `new_editor/app/Platform/Win32/Rendering/D3D12WindowRenderHost.cpp` 迁移来源: - `EditorWindowHostRuntime.cpp` 中的 Win32 建窗、ShowWindow、UpdateWindow - `EditorWindowWorkspaceCoordinator.cpp` 中的 `SetWindowTextW` - `EditorWindowChromeController.cpp` 中的窗口移动/缩放/最大化相关 Win32 调用 - `EditorWindowRuntimeController.*` 中的渲染宿主职责 投影规则: - `WindowApplicationRuntime` 只做依赖组装和主循环调度 - `WindowSession` / `WindowTopologyService` 只产生命令和 effect - `Win32WindowProjector` 把状态变化映射成 effect 列表 - `Win32WindowBackend` 执行 effect - `D3D12WindowRenderHost` 只执行渲染宿主职责 阶段验收: - 应用层不直接包含 Win32 行为代码 - render host 不直接承担内容决策 ### Phase 8 文件级动作 目标:删掉旧骨架,只保留新主干。 计划删除的旧文件范围: - `new_editor/app/Platform/Win32/Windowing/EditorWindowManager.*` - `new_editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.*` - `new_editor/app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.*` - `new_editor/app/Platform/Win32/Windowing/EditorWindowLifecycleCoordinator.*` - `new_editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.*` - `new_editor/app/Platform/Win32/Windowing/EditorUtilityWindowCoordinator.*` - `new_editor/app/Platform/Win32/Runtime/EditorWindowFrameDriver.*` - `new_editor/app/Platform/Win32/Windowing/EditorWindowTransferRequests.h` 高风险清理项: - `EditorContext` 中所有 per-window capability - 旧 chrome controller 中残留的状态机 - 所有 `TryGet*Binding` 调用点 - 所有 `friend class` 穿透 最终收口标准: - 旧桥只剩零或极薄 facade - include 方向符合第 8.1 节规则 - 代码库中查不到 transfer request 和 binding probe 主干调用 --- ## 10. `EditorContext` 重构要求 当前 `EditorContext` 必须被收缩,只保留全局状态和全局服务。 ### 可以继续保留在全局中的内容 - shell asset - selection service - command focus service - project runtime - scene runtime - system interaction host - session store ### 必须移出的内容 - per-window text measurer - per-window render host - per-window capture / presentation capability - 任何依赖“当前窗口实例”的局部能力 建议拆分为: - `EditorAppContext` - `EditorSessionStore` - per-window capability context --- ## 11. 测试策略 ## 11.1 测试 target 与目录规划 必须落成以下 target 规划: - `window_session_tests` - `window_topology_tests` - `frame_scheduler_tests` - `win32_message_translator_tests` - `new_editor_windowing_tests` 作为聚合 target 建议目录: - `new_editor/tests/windowing/window_session/` - `new_editor/tests/windowing/window_topology/` - `new_editor/tests/windowing/frame_scheduler/` - `new_editor/tests/platform/win32_message_translator/` ## 11.2 fake 组件要求 为保证这些测试不是空壳,必须同时定义: - `FakeWindowBackend` - 记录 effect 执行顺序,不实际调用 Win32 - `FakeRenderHost` - 记录 resize / render / present / feedback 顺序 - `FakeContentHost` - 可配置输出 `WindowContentOutput` - `FakeTopologyObserver` 或 `FakeTopologySink` - 校验 topology transaction 提交顺序 - `FakeFrameClock` - 控制 steady tick / priority frame 时序 ## 11.3 阶段门禁命令 计划执行时必须收口到明确命令,不能只写“补测试”。最低门禁如下: - 编译门禁: - `cmake --build build --config Debug --target XCUIEditorApp` - 单元测试门禁: - `cmake --build build --config Debug --target new_editor_windowing_tests` - `ctest --test-dir build --config Debug --output-on-failure -R "window_session|window_topology|frame_scheduler|win32_message_translator"` - 启动级冒烟: - 启动 `build/new_editor/Debug/XCUIEditor.exe`,确认能稳定启动并进入主循环 阶段要求: - Phase 0 结束前必须把这些命令和 target 写进计划。 - Phase 1 结束前必须把 target scaffold 和 fake 组件接口纳入 CMake 与目录结构。 - Phase 2 及之后每个阶段结束时,未通过本阶段门禁不得进入下一阶段。 ### 单元测试 必须补: - `WindowSession` 生命周期测试 - 输入 reducer 测试 - chrome reducer 测试 - topology service 拖拽事务测试 - frame scheduler 优先级测试 ### 平台适配测试 - `Win32WindowProcAdapter` - `Win32WindowProjector` - `Win32WindowBackend` effect 执行桩 ### 集成测试 至少覆盖: 1. 建窗 / 关窗 2. 主窗口关闭触发全局回收 3. resize / dpi change 后的帧调度 4. panel detach -> 新窗口创建 5. global tab drag -> cross-window drop 6. utility window open / reuse / focus ### 高频回归项 每阶段后都要回归: - capture 是否正确 - chrome hover / press / resize 是否正常 - resize 时是否抖动或错帧 - utility window 是否重复创建 - 跨窗口拖拽是否残留 preview - DPI 切换后 cursor / title / size 是否一致 --- ## 12. 风险与控制 ### 风险 A:新旧状态双写 控制: - 每阶段明确唯一可写状态源。 - 审核员重点检查是否存在双写。 ### 风险 B:移除即时帧后交互变钝 控制: - `FrameScheduler` 必须支持优先帧。 - resize / paint 恢复链必须做延迟对比。 ### 风险 C:拓扑迁移时语义漂移 控制: - 先冻结旧行为。 - 每个事务都要有明确测试和日志。 ### 风险 D:`EditorContext` 收缩影响外围模块 控制: - 先加新接口,再迁移调用点。 - 兼容桥必须有明确删除阶段。 --- ## 13. 完成定义 当且仅当以下条件同时满足时,重构完成: 1. 新窗口体系不存在 `friend` 穿透状态。 2. domain / application 中没有 Win32 类型泄漏。 3. `WndProc` 路径中不存在直接 render / present。 4. `EditorWindowFrameTransferRequests` 已删除。 5. `TryGet*Binding` 探测链已删除。 6. `WindowTopologyService` 是唯一跨窗口状态源。 7. `EditorContext` 不再挂载窗口局部能力。 8. 旧 coordinator / dispatcher / driver 已退出主干。 9. blocker / major 级审核评论全部关闭。 --- ## 14. 审核评论规则 这部分直接作为本计划的附录使用,规则保持简单。 ### 规则 1:评论必须带阶段和严重度 严重度只允许: - `BLOCKER` - `MAJOR` - `MINOR` - `QUESTION` ### 规则 2:评论必须有证据 证据至少包含一项: - 文件路径 - 类名 / 函数名 - 可复现行为 - 测试或日志 没有证据的评论不进入正式台账。 ### 规则 3:一条评论只打一个核心问题 不要把多个无关问题塞进同一条评论。 ### 规则 4:`BLOCKER` 直接卡阶段 有未关闭的 `BLOCKER` 时,不允许进入下一阶段。 ### 规则 5:`MAJOR` 必须在当前阶段关闭 `MAJOR` 不一定立即停工,但当前阶段结束前必须处理完。 ### 规则 6:执行者必须逐条回复 回复至少包含: - 处理动作 - 证据 - 当前剩余风险 ### 规则 7:评论只能追加,不删除历史 即使评论被驳回,也要保留记录。 ### 规则 8:计划变更必须先记评论区 如果实施过程中要改阶段目标、改边界、改顺序,必须先在评论区登记“计划变更请求”。 --- ## 15. 评论区 ### 15.1 使用说明 以下评论区用于登记: - 阶段开始 - 阶段结束 - 审核员评论 - 执行者回复 - 审核结论 - 计划变更请求 ### 15.2 状态总览 | Phase | Status | Start Date | End Date | Executor Summary | Reviewer Summary | | --- | --- | --- | --- | --- | --- | | Phase 0 | Not Started | | | | | | Phase 1 | Not Started | | | | | | Phase 2 | Not Started | | | | | | Phase 3 | Not Started | | | | | | Phase 4 | Not Started | | | | | | Phase 5 | Not Started | | | | | | Phase 6 | Not Started | | | | | | Phase 7 | Not Started | | | | | | Phase 8 | Not Started | | | | | ### 15.3 评论模板 ```md Comment ID: Phase: Severity: Status: Open Summary: Evidence: - Impact: Required Action: Close Condition: Reviewer: Date: ``` ### 15.4 回复模板 ```md Response To: Status: Answered Executor: Date: Action Taken: - Evidence: - Residual Risk: Requested Status: ``` ### 15.5 计划变更请求模板 ```md Plan Change Request ID: Affected Phase: Date: Executor: Reason: Requested Change: Impact: - Rollback Condition: ``` --- ## 16. 阶段评论记录区 ## Phase 0 ### Start - Date: - Executor: - Goal: - Exit Criteria: ### Comments _No open comments. 2026-04-23 的 REVIEW-P0-001 ~ REVIEW-P0-006 已吸收到计划正文。_ ### End - Date: - Executor: - Requested Review Decision: ### Reviewer Decision - Decision: - Reviewer: - Notes: --- ## Phase 1 ### Start - Date: - Executor: - Goal: - Exit Criteria: ### Comments _No comments yet._ ### End - Date: - Executor: - Requested Review Decision: ### Reviewer Decision - Decision: - Reviewer: - Notes: --- ## Phase 2 ### Start - Date: - Executor: - Goal: - Exit Criteria: ### Comments _No comments yet._ ### End - Date: - Executor: - Requested Review Decision: ### Reviewer Decision - Decision: - Reviewer: - Notes: --- ## Phase 3 ### Start - Date: - Executor: - Goal: - Exit Criteria: ### Comments _No comments yet._ ### End - Date: - Executor: - Requested Review Decision: ### Reviewer Decision - Decision: - Reviewer: - Notes: --- ## Phase 4 ### Start - Date: - Executor: - Goal: - Exit Criteria: ### Comments _No comments yet._ ### End - Date: - Executor: - Requested Review Decision: ### Reviewer Decision - Decision: - Reviewer: - Notes: --- ## Phase 5 ### Start - Date: - Executor: - Goal: - Exit Criteria: ### Comments _No comments yet._ ### End - Date: - Executor: - Requested Review Decision: ### Reviewer Decision - Decision: - Reviewer: - Notes: --- ## Phase 6 ### Start - Date: - Executor: - Goal: - Exit Criteria: ### Comments _No comments yet._ ### End - Date: - Executor: - Requested Review Decision: ### Reviewer Decision - Decision: - Reviewer: - Notes: --- ## Phase 7 ### Start - Date: - Executor: - Goal: - Exit Criteria: ### Comments _No comments yet._ ### End - Date: - Executor: - Requested Review Decision: ### Reviewer Decision - Decision: - Reviewer: - Notes: --- ## Phase 8 ### Start - Date: - Executor: - Goal: - Exit Criteria: ### Comments _No comments yet._ ### End - Date: - Executor: - Requested Review Decision: ### Reviewer Decision - Decision: - Reviewer: - Notes: