Files
XCEngine/editor/AGENTS.md

9.8 KiB

Editor Agent Guide

This file is for agents working in editor/** and tests/UI/Editor/**. Treat the current code and tests as source of truth. If this guide drifts from the checkout, update it in the same change.

Current Priority

The editor is not in a "split more layers for their own sake" phase.

The primary product line is still runtime/product loop closure:

  • extend the bound EditorRuntimeCoordinator instead of adding panel-local shortcuts for run.*, scene document commands, or project scene opens
  • keep play mode transactional: EditorRuntimeCoordinator must enter play through EditorSceneRuntime::BeginPlaySession, and RuntimeLoop must run the play-session runtime scene rather than the editable document scene
  • drive EditorRuntimeCoordinator from the app frame pump exactly once per outer frame; do not tick runtime from per-window shell/content update paths
  • keep scripts.* coordinator-owned and capability-driven. scripts.rebuild must evaluate/dispatch through the bound scripting runtime service and expose the real availability/failure message rather than a hardcoded stub
  • keep Game, Scene, Inspector, Selection, and Console coherent across runtime transitions

Do not burn time on broad architectural churn unless it directly unlocks that product loop or removes an active architectural hazard.

Engine Boundary Status

The old public EditorEngineServices god interface is gone.

The current editor-to-engine boundaries are narrow and must stay narrow:

  • EditorSceneBackendFactory
  • SceneViewportEngineBridge
  • GameViewportEngineBridge
  • EditorShaderProvider
  • EditorEngineLifecycle

EngineEditorComposition in editor/app/Services/Engine/EngineEditorServices.* is an internal composition root that hands out those narrow interfaces. It is not a new shared facade for features to depend on.

Rules:

  • do not recreate EditorEngineServices
  • do not add a replacement god interface under a different name
  • do not make new panels, passes, or runtimes depend on a catch-all engine service locator
  • keep render-pass dependencies low-level: passes may depend on EditorShaderProvider, not on scene backend creation or lifecycle code
  • composition owns assembly; features and passes must receive explicit dependencies

Product Truths

  • GameViewportFeature, GameViewportRenderService, and related tests exist. EditorRuntimeCoordinator is now the app-level owner for play-mode command routing and uses RuntimeLoop for the active play-session scene.
  • Play mode is a scene transaction. The engine scene backend snapshots the editable scene, replaces it with a runtime scene for play/step, and restores the editable scene when the play session ends. Do not start runtime playback directly from EditorSceneRuntime::GetActiveScene().
  • EditorHostCommandBridge delegates file.*, run.*, and scripts.* to a bound runtime owner. If no owner is bound, it must continue exposing honest disabled messages.
  • host-command context is workspace-shell local, not editor-global. The active panel fallback and command-focus resolution must come from the current UIEditorWorkspaceController plus that shell's EditorCommandFocusService. Do not put window-local command routing back on EditorSession, EditorContext, or any app-global bridge/shortcut manager.
  • EditorSceneRuntime owns raw scene editing behavior and scene mutations. It may return transient startup/open/new results, but it must not own the live scene document state. Scene viewport private state now lives in SceneViewportSession, owned by the scene viewport feature/panel instance, not by EditorContext, EditorFrameServices, or the shared scene runtime. EditorSceneRuntime also owns the unified scene-edit transaction/history layer: hierarchy edits, inspector mutations, and scene gizmo commits must all flow through the same backend snapshot-based undo/redo path rather than panel-local or transform-only history. EditorRuntimeCoordinator owns scene document state: current path/name, dirty flag, new/open/save routing, and runtime-mode transitions.
  • EditorRuntimeCoordinator time advancement is app-owned. It must tick once per outer application frame before window rendering, not once per workspace window or once per shell update.
  • ProjectPanel may identify an openable scene asset, but scene document loading must go through the bound typed scene-open request callback and the coordinator. Do not reintroduce ProjectRuntime pending-open queues.
  • scripts.rebuild is coordinator-owned and routes through the bound scripting-runtime service. Keep its evaluation/dispatch honest: expose the live capability/failure message from that service rather than a hardcoded placeholder.
  • the old shared EditorPanelServices dependency bag is gone. Workspace and utility panels now receive explicit bindings or panel-local contexts; do not recreate a catch-all mutable panel service bundle under a new name
  • EditorFrameServices remains a window/content orchestration boundary. It must stay on the window/content and shell-orchestration seam only. It must not be passed through the workspace-panel seam or the utility-window panel seam as a generic dependency source; workspace panels and utility panels should receive explicit panel-local bindings or typed callbacks instead. Runtime time-step ownership does not belong on this interface. Command routing ownership also does not belong on this interface: do not add host-command bridge bindings, command-focus syncing, or active-route/session transport back to EditorFrameServices.

Working Rules

  • prefer explicit owner/coordinator/runtime-service seams over direct panel to engine hookups
  • keep panel dependencies explicit. If a panel needs more data or actions, add a narrow panel-local context or binding instead of routing everything through a generic shared services struct
  • keep utility-window dependencies explicit. Color picker, add-component, and future utility panels must be constructed with the exact runtime/tool state they use; do not make utility panel update paths depend on EditorFrameServices
  • keep command routing shell-local. UIEditorShortcutManager, EditorHostCommandBridge, and EditorCommandFocusService must be owned by the workspace shell/runtime set that serves one window. If a command needs active-panel context, pass the current UIEditorWorkspaceController; do not mirror that context into EditorSession
  • keep viewport-local state local. Camera/tool mode/pivot/space state for Scene belongs in SceneViewportSession on the feature/controller side; do not push it back into shared runtime/context/frame-service layers for convenience
  • do not reintroduce void* plus callback-function panel hooks for scene-open or utility-window requests; use typed callbacks or explicit requester interfaces instead
  • when changing runtime or document flow, inspect EditorSession, EditorRuntimeCoordinator, EditorHostCommandBridge, the relevant feature, and the matching tests
  • if a change affects ownership, state flow, or command routing, add or update tests in tests/UI/Editor/unit
  • keep multi-window work behind runtime/document correctness; do not move it ahead of core ownership closure

Do Not Regress

  • no new service-locator style editor-to-engine dependency
  • no new low-level render pass dependency on scene backend creation or lifecycle
  • no silent fallback where a command should fail honestly
  • no panel-local shortcut that bypasses the intended runtime owner
  • no new shared mutable panel-services bag or service-locator style panel dependency bundle
  • no EditorFrameServices tunneling into workspace-panel update/prepare paths
  • no EditorFrameServices tunneling into utility-window panel update paths
  • no global host-command bridge or global shortcut manager shared across editor windows
  • no EditorSession.activePanelId, EditorSession.activeRoute, or equivalent session mirrors for per-window command context
  • no per-window or per-shell runtime ticking path; runtime must not advance once per workspace window
  • no new live scene document metadata on EditorSceneRuntime; coordinator remains the single owner for current path/name/dirty state
  • no new path where Scene viewport render requests or camera/tool state are recomputed from EditorContext instead of the owning SceneViewportSession
  • no scene-open side channel in EditorProjectRuntime; project UI must call the panel service request hook
  • no play-mode path that mutates the editable scene in place or skips EditorScenePlaySession
  • no new transform-only or panel-local scene undo stack; all scene mutations must record through EditorSceneRuntime's unified snapshot transaction history

Good Entry Points

  • editor/app/Bootstrap/Application.cpp
  • editor/app/Composition/EditorContext.cpp
  • editor/app/Composition/EditorShellRuntime.cpp
  • editor/app/Services/Runtime/EditorRuntimeCoordinator.cpp
  • editor/app/Rendering/Viewport/ViewportHostService.cpp
  • editor/app/Services/Engine/EngineEditorServices.h
  • editor/app/Services/Scene/EditorSceneRuntime.cpp
  • editor/app/Core/Commands/EditorHostCommandBridge.cpp
  • docs/plan/editor-next-stage-runtime-plan.md
  • docs/plan/editor-engine-services-refactor-plan.md

Verification

Minimum verification for editor app/core changes:

cmake --build build --config Debug --target editor_app_core_tests
cmake --build build --config Debug --target editor_app_feature_tests

When the executable path is affected, also rebuild:

cmake --build build --config Debug --target XCEditor

Relevant focused tests include:

  • test_editor_host_command_bridge.cpp
  • test_editor_runtime_coordinator.cpp
  • test_game_viewport_runtime.cpp
  • test_project_panel.cpp
  • test_scene_viewport_render_plan.cpp
  • test_scene_viewport_runtime.cpp