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

48 KiB
Raw Blame 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

但这些状态不由它自己独占修改,而是被 MessageDispatcherChromeControllerHostRuntimeLifecycleCoordinatorWorkspaceCoordinator 等对象直接穿透读写。

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 实际只保存了 HWNDwindowIdtitleprimarylifecycle。输入、chrome、resize、dpi、capture、frame、content 相关真实状态都不在这个“状态对象”里,这说明系统从命名层面就在伪装一个并不存在的统一状态边界。
  2. new_editor/app/Platform/Win32/Windowing/EditorWindow.h 通过 friend classEditorWindowChromeControllerEditorWindowFrameDriverEditorWindowHostRuntimeEditorWindowMessageDispatcherEditorWindowLifecycleCoordinatorEditorWindowWorkspaceCoordinator 全部放进 EditorWindow 内部。这意味着窗口对象没有封装,只是一个公开状态袋。
  3. new_editor/app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.cppWM_DPICHANGEDWM_EXITSIZEMOVEWM_SIZEWM_PAINT 中直接触发 RenderAndHandleWindowFrameOnPaintMessage。也就是说 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 暴露 TryGetWorkspaceBindingTryGetDockHostBindingTryGetInputFeedbackBindingTryGetTitleBarBinding。应用层必须去“探测内容层支持哪些能力”,再临时决定走哪条逻辑分支,本质上是拿 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 头文件中禁止出现 HWNDWPARAMLPARAMRECTPOINT
  3. WndProc 路径中禁止直接 render / present。
  4. EditorWindowFrameTransferRequests 必须删除。
  5. TryGetWorkspaceBinding / TryGetDockHostBinding / TryGetInputFeedbackBinding / TryGetTitleBarBinding 必须退出主干架构。
  6. 跨窗口状态必须显式保存在 WindowTopologyState 中,禁止从 live windows 反推。
  7. EditorContext 中禁止挂载窗口私有能力对象。

4. 目标架构

4.1 分层模型

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 抽象

建议新增目录:

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

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

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

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_PAINTWM_SIZEWM_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

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

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. 目录重组建议

建议新增目录:

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 依赖与边界规则

重构过程中必须同时执行“依赖收口”,否则类名换了,旧问题会原样复活。

允许的依赖方向

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 禁止直接调用 CreateWindowExWSetWindowPosSetCapturePresent
  3. Windowing/Content 禁止感知 HWNDRECTPOINTWPARAMLPARAM
  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. 新增 WindowStateWindowTopologyStateWindowEventWindowIntentWindowEffect
  2. 新增 WindowContentInputWindowContentOutput
  3. 新增纯 reducer 雏形:
    • lifecycle
    • input
    • chrome
  4. 保持旧主链不动。

退出条件

  • 新类型不依赖 Win32 类型。
  • 新类型足以表达旧主链中的关键意图。
  • 已把 window_session_testswindow_topology_testsframe_scheduler_testswin32_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、FrameReasonFramePriority
  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
  2. 新建 Win32WindowBackend
  3. 新建 Win32WindowProjector
  4. 新建 D3D12WindowRenderHost
  5. Application 收缩为 bootstrap主循环调度迁给 WindowApplicationRuntime
  6. 所有 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
  • EditorWindowMessageDispatcherm_inputController / m_chromeController / m_runtime 的直接访问
  • EditorWindowFrameDriverwindow.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
  • FakeTopologyObserverFakeTopologySink
    • 校验 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拓扑迁移时语义漂移

控制:

  • 先冻结旧行为。
  • 每个事务都要有明确测试和日志。

风险 DEditorContext 收缩影响外围模块

控制:

  • 先加新接口,再迁移调用点。
  • 兼容桥必须有明确删除阶段。

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一条评论只打一个核心问题

不要把多个无关问题塞进同一条评论。

规则 4BLOCKER 直接卡阶段

有未关闭的 BLOCKER 时,不允许进入下一阶段。

规则 5MAJOR 必须在当前阶段关闭

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 评论模板

Comment ID:
Phase:
Severity:
Status: Open

Summary:

Evidence:
- 

Impact:

Required Action:

Close Condition:

Reviewer:
Date:

15.4 回复模板

Response To:
Status: Answered
Executor:
Date:

Action Taken:
- 

Evidence:
- 

Residual Risk:

Requested Status:

15.5 计划变更请求模板

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: