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

1888 lines
48 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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: