Files
XCEngine/docs/plan/NewEditor_Win32WindowArchitectureRefactorPlan_2026-04-23.md

1888 lines
48 KiB
Markdown
Raw Normal View History

# 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<WindowEvent> 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: