Files
XCEngine/editor/AGENTS.md

398 lines
19 KiB
Markdown

# XCUI Editor Agent Guide
This file documents the current editor architecture for agents working under
`editor/`. It describes the code that exists in this checkout, not a desired
future shape.
If this file conflicts with the current code, `editor/CMakeLists.txt`, or the
real directory tree, trust the code and update this file in the same change.
## Build Shape
The important production targets are:
- `XCUIEditorLib`: reusable XCEditor framework code from `editor/src` and
`editor/include/XCEditor`, including the reusable window authority,
synchronization planner, and presentation projection under
`XCEditor/Windowing`.
- `XCUIEditorAppWindowing`: app-internal static library for the current
host contracts, content controllers, coordinators, frame transfer flow,
backend-neutral runtime controllers, and window manager. It consumes render
services through `editor/app/Rendering/Host` interfaces; it must not include
concrete D3D12 or Win32 rendering implementation headers.
- `XCUIEditorApp`: concrete editor executable. Its output name is `XCEngine`.
Do not invent target boundaries that are not present in `editor/CMakeLists.txt`.
Names such as `XCUIEditorAppCore`, `XCUIEditorAppLib`, and `XCUIEditorHost`
are not current production library boundaries in this checkout.
There is now a narrow public `editor/include/XCEditor/Windowing` layer for the
generic window authority model, synchronization plan/planner, and presentation
projection. App-specific window orchestration remains under
`editor/app/Windowing`, render host contracts remain under
`editor/app/Rendering/Host`, the concrete D3D12 renderer remains under
`editor/app/Rendering/D3D12`, and the concrete Win32 host remains under
`editor/app/Platform/Win32`.
## Layering
Use these ownership boundaries when changing code:
- `editor/include/XCEditor` and `editor/src`: reusable editor framework.
Keep this layer independent from app state, Win32, D3D12 host code, and
`App::*` types.
- `editor/include/XCEditor/Windowing` and `editor/src/Windowing`: reusable
window authority, validation, synchronization planning, commit, and
presentation projection. Keep this layer generic and free of app content,
host runtime, native platform, and rendering details.
- `editor/app/Composition`, `Commands`, `Features`, `Project`, `Scene`,
`State`, `System`, `UtilityWindows`: editor product semantics.
- `editor/app/Windowing/Content`, `Coordinator`, `Frame`, `Host`, `Runtime`:
app window orchestration, content ownership, frame driving, frame transfer,
and per-window runtime state. Keep this layer backend-neutral; it may depend
on `editor/app/Rendering/Host` contracts, but not concrete render backends.
- `editor/app/Platform/Win32`: native window, message dispatch, input,
lifecycle, chrome, HWND ownership, and native host adapter behavior.
- `editor/app/Rendering/Host`: app rendering contracts consumed by app
windowing and app content, including the render-runtime factory interface.
- `editor/app/Rendering/D3D12`: concrete D3D12 window renderer, UI renderer,
texture host, text system, render loop, render-runtime adapter, and
render-runtime factory.
The semantic dependency direction should remain:
```text
XCEditor framework
<- editor app semantics
<- app windowing runtime / rendering host contracts
<- application composition root / concrete Win32 and rendering adapters
```
## Startup Flow
The application starts through:
```text
app/main.cpp
-> RunXCUIEditorApp
-> Application::Run
-> Application::Initialize
```
The primary workspace window is initialized through:
```text
EditorContext::BuildWorkspaceController()
-> EditorWindowSystem::BootstrapPrimaryWindow(...)
-> EditorWindowManager::CreateWorkspaceWindow(...)
-> EditorWindowContentFactory::CreateWorkspaceContentController(...)
-> EditorWindowRenderRuntimeFactory::CreateWindowRenderRuntime()
-> EditorWindowRuntimeController(EditorContext, contentController, renderRuntime)
-> EditorWindowInstance(runtimeController)
-> EditorWindowHostRuntime::CreateHostWindow(windowInstance, ...)
-> Win32 EditorWindow native peer
```
Keep authoritative window bootstrap in `EditorWindowSystem`; do not move it
back into the Win32 host.
## Frame Runtime Ownership
`EditorWindowManager` owns steady-state frame iteration and the immediate-frame
callbacks used by native paint, resize, maximize/restore, and chrome actions.
The Win32 host may request a frame through `EditorWindowHostCoordinator`, but it
must not own the editor frame loop or fetch `EditorContext`.
`EditorWindowRuntimeController` lives under `editor/app/Windowing/Runtime`.
App windowing creates it from `EditorContext` plus a workspace or utility
content controller plus a backend-neutral `EditorWindowRenderRuntime`, then
wraps it in app-owned `EditorWindowInstance`. The Win32 host receives the
instance only as an `EditorHostWindow` owner/native-peer binding; it must not
store or call `EditorWindowRuntimeController` directly, create content
controllers, or receive `EditorContext`.
Concrete renderer selection is composed above both app windowing and the native
host. `Application` creates the current
`D3D12EditorWindowRenderRuntimeFactory` and passes it into
`EditorWindowManager`; app windowing asks the factory for backend-neutral
`Rendering::Host::EditorWindowRenderRuntime` instances. `EditorWindowInstance`
owns runtime controllers that already contain their render runtime; the Win32
host must not create concrete render backends or own render runtimes.
`EditorWindowRuntimeController` may call
`EditorWindowRenderRuntime`, `UiTextureHost`, and `ViewportRenderHost`, but it
must not include `Rendering/D3D12` headers, `windows.h`, or HWND types.
The host contract direction is:
```text
EditorWindowManager / coordinators
-> ask EditorWindowRenderRuntimeFactory for EditorWindowRenderRuntime
-> create EditorWindowRuntimeController
-> wrap it in app-owned EditorWindowInstance
-> EditorWindowHostRuntime::CreateHostWindow(windowInstance, ...)
-> Win32 EditorWindow owns HWND/input/chrome and calls back through native-peer contract
```
## Window Authority Model
`EditorWindowSystem` owns the authoritative `UIEditorWindowWorkspaceSet` through
`EditorWindowWorkspaceStore`.
Normal per-frame workspace edits now use direct writes into that authoritative
state:
```text
EditorWorkspaceWindowContentController::UpdateAndAppend(...)
-> EditorWindowSystem::TryBuildLiveWindowWorkspaceController(windowId, ...)
-> UIEditorWorkspaceController::BindToState(...)
-> workspace operations mutate the bound authoritative workspace/session
```
The old normal path is obsolete:
```text
snapshot diff
-> workspaceMutation frame request
-> coordinator
-> synchronization plan
-> commit
```
Do not reintroduce `workspaceMutation` as the steady-state route for ordinary
layout, tab, visibility, or active-panel edits inside an existing workspace
window.
Cross-window create, update, close, and destroyed-window reconciliation still
use the planner/coordinator flow:
```text
target UIEditorWindowWorkspaceSet
-> EditorWindowSystem::BuildPlanForWindowSet(...)
-> EditorWindowWorkspaceCoordinator::ApplySynchronizationPlan(...)
-> EditorWindowSystem::CommitSynchronizationPlan(...)
```
`EditorWindowWorkspaceCoordinator::RefreshWindowPresentation(...)` refreshes
each workspace window projection from authoritative state. Presentation should
follow authority; it should not become a second source of truth.
## Frame Transfer Requests
`EditorWindowFrameTransferRequests` is only for host-side or cross-window side
effects that cannot be represented as direct in-window workspace edits.
Current request fields are:
- `workspace.beginGlobalTabDrag`
- `workspace.detachPanel`
- `utility.openUtilityWindow`
There is no `workspace.workspaceMutation` field in the current frame transfer
contract. Treat any doc, comment, or test name that says otherwise as stale.
## Window Categories
Workspace and utility windows share the native host path but use distinct
content controllers.
- Workspace windows use `EditorWorkspaceWindowContentController`.
- Utility windows use `EditorUtilityWindowContentController`.
- App windowing creates content through `EditorWindowContentFactory`.
- App windowing wraps content in `EditorWindowRuntimeController`, then owns that
runtime through `EditorWindowInstance`.
- The concrete host creates a native peer for an existing app-owned
`EditorHostWindow`; it does not receive or validate runtime controllers.
Utility windows are descriptor driven through `EditorUtilityWindowDescriptor`,
`EditorUtilityWindowRegistry`, and `CreateEditorUtilityWindowPanel(...)`.
Register new utility windows there rather than hard-coding them in Win32 host
logic.
## Modification Rules
- First decide whether the change belongs to XCEditor framework, app semantics,
app windowing, Win32 host, or rendering host.
- Keep public framework headers free of `App::*`, Win32, and D3D12 host types.
- Do not add `editor/app` as an include directory for `XCUIEditorLib`.
App-only services must cross into framework code through framework-owned
interfaces and app-side adapters.
- Do not compile `${XCUI_EDITOR_SHARED_SOURCES}` directly into `XCUIEditorApp`;
the executable should consume shared framework code through `XCUIEditorLib`.
- Use `editor/include/XCEditor/Windowing` and `editor/src/Windowing` for
generic window authority, synchronization, validation, and presentation
projection.
- Use `editor/app/Windowing` for app-specific window runtime semantics,
content, frame transfer, host contracts, runtime controllers, and
coordinators.
- Use `editor/app/Rendering/Host` for renderer-facing contracts that app
windowing or app content may consume, including
`EditorWindowRenderRuntimeFactory`.
- Use `editor/app/Rendering/D3D12` for concrete D3D12 renderer ownership,
swap-chain/present/capture details, UI texture/text/render systems, and the
D3D12 `EditorWindowRenderRuntime` implementation and factory.
- Use `editor/app/Platform/Win32` only for native host behavior and message
integration. Win32 code may request frames through the host coordinator; it
must not own the editor frame loop, expose `EditorContext`, or create
concrete render backends.
- Do not let `editor/app/Windowing` include `Platform/Win32` headers.
- Do not let `editor/app/Windowing` include `Rendering/D3D12` headers,
`windows.h`, or HWND types. It should consume concrete rendering only through
`Rendering::Host::EditorWindowRenderRuntime` and
`Rendering::Host::EditorWindowRenderRuntimeFactory`.
- Do not let `editor/app/Platform/Win32` include `Rendering/D3D12` headers or
implement render-runtime factory methods on the native host interfaces.
- Do not spread D3D12 host types into app windowing content, coordinator,
frame-transfer, runtime-controller, or public host-contract headers.
- Do not let Win32 host code create workspace or utility content directly.
It should receive an app-owned `EditorHostWindow`/`EditorWindowInstance`
created by app windowing, not an `EditorWindowRuntimeController`.
- Use direct authoritative workspace binding for ordinary in-window workspace
mutations.
- Use synchronization plans for operations that create, close, replace, or
reconcile workspace windows.
- Use frame transfer requests only for host-side or cross-window effects.
## Current Architecture Debt
The highest-value windowing boundary has been hardened: `XCUIEditorLib` now
owns the reusable window authority, synchronization planner, workspace store,
and presentation projection under `XCEditor/Windowing`. `XCUIEditorAppWindowing`
owns the app-internal content controller factory, runtime controllers,
coordinators, frame transfer flow, host contracts, and manager. The Win32 host
is the concrete native adapter and creates native host windows for app-owned
`EditorWindowInstance` objects supplied by app windowing.
The framework/app compile boundary is also enforced. `XCUIEditorLib` does not
receive `editor/app` includes, and `XCUIEditorApp` does not directly compile
the shared framework sources. When framework UI code needs app-provided data
such as loaded icons, expose a framework-owned interface and adapt it in app
composition code.
The main windowing debt that was previously in the Win32 host has been cut:
frame iteration, immediate frame driving, content-controller construction, and
`EditorContext` ownership now live in app windowing. `EditorWindowHostCoordinator`
does not expose `GetEditorContext()`, and the native host receives an
app-owned `EditorWindowInstance` rather than raw workspace/utility content or a
runtime controller.
The native-peer ownership cut has also been made: `EditorWindowManager` owns
`EditorWindowInstance` objects, each instance owns its
`EditorWindowRuntimeController`, and `editor/app/Platform/Win32/Windowing`
`EditorWindow` is a native peer for HWND/input/chrome/message integration. The
Win32 platform code must stay free of
`Windowing/Runtime/EditorWindowRuntimeController.h`.
The renderer ownership cut has also been made: `XCUIEditorAppWindowing` no
longer owns the concrete D3D12 window render loop or Win32 surface setup.
`EditorWindowRuntimeController` consumes a backend-neutral
`Rendering::Host::EditorWindowRenderRuntime`, and `EditorWindowManager` receives
a backend-neutral `Rendering::Host::EditorWindowRenderRuntimeFactory`. The
current D3D12 factory is composed in `Application`, not in the Win32 host.
The remaining promotion debt is the app runtime surface itself.
`XCUIEditorAppWindowing` is still app-internal and may depend on app semantics
such as `EditorContext`, `EditorShellRuntime`, utility window descriptors, and
product-specific content. Do not promote host interfaces, frame transfer,
runtime controllers, content controllers, or render-runtime contracts to
`XCEditor` until they are generic enough to expose. Do not move frame ownership
back into `editor/app/Platform/Win32`, do not move concrete renderer ownership
back into `editor/app/Windowing`, and do not move renderer factory ownership
back into the Win32 host.
Do not take another architecture cut just to keep carving this area. After the
native-peer ownership boundary, the next default improvement should be boundary
guardrails, such as checks that keep `editor/app/Windowing` free of
`Rendering/D3D12`, `Platform/Win32`, `windows.h`, and HWND types, and keep
`editor/app/Platform/Win32` free of `Rendering/D3D12` and
`Windowing/Runtime/EditorWindowRuntimeController.h`. Consider another structural
cut only when there is concrete pressure, such as another native host, another
render backend, or headless editor/window tests.
## Validation
Default editor validation is only the app build plus the 12-second smoke run:
```powershell
cmake --build build --config Debug --target editor_ui_smoke_targets
build\tests\UI\Editor\smoke\Debug\editor_ui_smoke_runner.exe build\editor\Debug\XCEngine.exe
```
Do not run `editor_windowing_phase1_tests`, `editor_ui_tests`, or broader test
targets by default. Run them only when the user explicitly asks for them or a
separate targeted change makes them necessary.
The runner sets `XCUIEDITOR_SMOKE_TEST_DURATION_SECONDS=12`, waits for the
editor to launch, lets the app auto-exit, and treats a clean editor exit as
success.
## Recommended Reading
Start with these files for editor/windowing work:
- `editor/CMakeLists.txt`
- `editor/app/Bootstrap/Application.*`
- `editor/app/Composition/EditorContext.*`
- `editor/app/Composition/EditorShellRuntime.*`
- `editor/include/XCEditor/Workspace/UIEditorWorkspaceController.h`
- `editor/src/Workspace/UIEditorWorkspaceController.cpp`
- `editor/include/XCEditor/Windowing/System/EditorWindowSystem.h`
- `editor/include/XCEditor/Windowing/System/EditorWindowSynchronizationPlan.h`
- `editor/include/XCEditor/Windowing/System/EditorWindowSynchronizationPlanner.h`
- `editor/include/XCEditor/Windowing/Presentation/EditorWorkspaceWindowProjection.h`
- `editor/include/XCEditor/Windowing/Presentation/EditorWindowPresentationPolicy.h`
- `editor/src/Windowing/System/EditorWindowSystem.cpp`
- `editor/src/Windowing/System/EditorWindowWorkspaceStore.*`
- `editor/src/Windowing/System/EditorWindowSynchronizationPlanner.cpp`
- `editor/src/Windowing/Presentation/EditorWindowPresentationPolicy.cpp`
- `editor/app/Windowing/Host/EditorWindowHostInterfaces.h`
- `editor/app/Windowing/Content/EditorWindowContentController.h`
- `editor/app/Windowing/Content/EditorWindowContentFactory.*`
- `editor/app/Windowing/Content/EditorWorkspaceWindowContentController.*`
- `editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.*`
- `editor/app/Windowing/Frame/EditorWindowTransferRequests.h`
- `editor/app/Windowing/Runtime/EditorWindowRuntimeController.*`
- `editor/app/Windowing/Runtime/EditorWindowScreenshotController.*`
- `editor/app/Windowing/EditorWindowInstance.*`
- `editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.*`
- `editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.*`
- `editor/app/Windowing/EditorWindowManager.*`
- `editor/app/Rendering/Host/EditorWindowRenderRuntime.h`
- `editor/app/Rendering/D3D12/D3D12EditorWindowRenderRuntime.*`
- `editor/app/Platform/Win32/Windowing/EditorWindow.*`
- `editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.*`
- `editor/app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.*`
- `tests/UI/Editor/smoke/CMakeLists.txt`
## Recent Cuts
- The framework-to-app icon dependency is sealed: `UIEditorShellCompose` uses
the framework-owned `UIEditorShellIconResolver`, and the app adapts
`BuiltInIcons` in `EditorShellDrawComposer`.
- The framework/app compile boundary is sealed: `XCUIEditorLib` does not receive
`editor/app` includes, `XCUIEditorApp` does not directly compile
`${XCUI_EDITOR_SHARED_SOURCES}`, and the app consumes framework code through
`XCUIEditorLib`.
- The reusable window authority core lives in the framework:
`EditorWindowSystem`, synchronization planning, workspace store, and
presentation projection are under `editor/include/XCEditor/Windowing` and
`editor/src/Windowing`.
- App windowing owns the runtime cut: content controllers,
`EditorWindowRuntimeController`, frame driving, frame transfer, host
contracts, and `EditorWindowManager` are under `editor/app/Windowing`; Win32
remains the native adapter and no longer exposes `EditorContext`.
- The native-peer ownership cut is sealed: app windowing now owns live
`EditorWindowInstance` objects, those instances own
`EditorWindowRuntimeController`, and Win32 `EditorWindow` only owns the
native peer side for HWND/input/chrome/message work. `CreateHostWindow(...)`
binds a native peer to an existing `EditorHostWindow`; it does not receive a
runtime controller.
- The concrete renderer cut is sealed: app windowing consumes
`Rendering::Host::EditorWindowRenderRuntime` and
`Rendering::Host::EditorWindowRenderRuntimeFactory`, while the current D3D12
implementation and factory live under `editor/app/Rendering/D3D12`. The
factory is composed in `Application`; `editor/app/Windowing` should stay free
of `Rendering/D3D12`, `windows.h`, and HWND types, and
`editor/app/Platform/Win32` should stay free of `Rendering/D3D12`.
- Default validation remains the editor app build plus the 12-second smoke run;
run broader windowing/unit targets only for targeted coverage.