28 KiB
Modernization Task Tracker
Status: live Last updated: 2026-06-17
This file is the active execution queue. It is written for a coordinator that
can assign bounded packets to smaller parallel workers. Completed and stale
history belongs in docs/modernization/tasks-done.md, not here.
Operating Rules
- Prioritize working-app ownership transfer over planners, CLI commands, package-only cleanup, or test-only work.
- Every coding task must remove or narrow a real retained dependency, hotspot, unsafe ownership path, or thread/runtime ambiguity.
- Tests are validation and guardrails. A task that only adds tests is not a P0 modernization slice unless it directly enables a blocked ownership move.
- Do not broaden worker scopes. If a task crosses file boundaries, split it into worker packets with disjoint write scopes and integrate centrally.
- No new
App::I,Canvas::I, owning rawNode*, detached worker, direct GL resource dependency, or platform SDK dependency may be introduced in moved code. - Raw pointers may remain only as documented non-owning implementation details backed by a checked owner, handle, scoped connection, or explicit lifetime contract.
- Preserve current app behavior first. UI appearance, file formats, brush behavior, platform behavior, and rendering output are not to be redesigned in modernization slices.
- Use CMake source ownership as the progress signal. Shrinking
PP_PANOPAINTER_*andPP_LEGACY_*ownership matters more than adding new helpers around the same retained code.
Current Audit Snapshot
Validation performed during the 2026-06-17 review:
python scripts/dev/check_component_boundaries.py: passed.python scripts/dev/check_renderer_api_contract.py: passed.
Key facts:
- Pure component boundaries currently pass their static checks.
- Remaining architectural risk is concentrated in the working app, retained app/UI/canvas targets, singleton reach, raw node ownership, and direct GL resource usage.
PP_PANOPAINTER_APP_SOURCES: 47 files, about 9620 lines.PP_PANOPAINTER_UI_SOURCES: 52 files, about 9051 lines.PP_LEGACY_PAINT_DOCUMENT_SOURCES: 22 files, about 6277 lines.PP_LEGACY_APP_SOURCES: 26 files, about 4711 lines.PP_LEGACY_UI_CORE_SOURCES: 32 files, about 4304 lines.App::Istill appears hundreds of times in retained app/canvas/UI/resource code.Canvas::Istill appears hundreds of times in retained canvas modes, panels, and workflow bridges.- Raw
Node*and callback captures remain a dominant UI lifetime risk. - Retained stroke-preview/runtime draw paths still depend on legacy
render/runtime helpers, but
RTT,Texture2D,Shape,Shader,TextMesh, andCanvasLayerno longer callApp::Idirectly for queueing. AppRuntimenow owns synchronized running flags plus explicit post/reject, same-thread execution, and queue-drain behavior, but broader singleton reach and app-shell ownership remain.- Retained cloud upload/download, brush-package import, and timelapse-export
async paths now route through
AppRuntime::canvas_async_task, but dialog and execution ownership still remains in retained app/document/cloud bridges. App::dialog_browse()no longer owns browse-dialog button wiring inline; the retained document-open bridge now owns that handoff insrc/legacy_document_open_services.*.App::init_toolbar_main()now delegates retained main-toolbar button wiring throughsrc/legacy_main_toolbar_binding_services.*, sosrc/app_layout_main_toolbar.cppis down to a thin root lookup and adapter call while retained toolbar execution still lives insrc/legacy_app_shell_services.*.App::init_menu_file()now delegates retained File-menu popup and export submenu wiring throughsrc/legacy_file_menu_binding_services.*, sosrc/app_layout_file_menu.cppis down to a thin trigger lookup and adapter call while retained file/export execution still lives insrc/legacy_app_shell_services.*.App::init_menu_about()now delegates retained About-menu popup wiring throughsrc/legacy_about_menu_binding_services.*, sosrc/app_layout_about_layer_menu.cppno longer owns the About callback body inline while retained About execution still lives insrc/legacy_app_shell_services.*.App::init_menu_tools()now delegates the retained Tools > Panels submenu wiring throughsrc/legacy_tools_menu_binding_services.*, sosrc/app_layout_tools_menu.cppno longer owns that floating-panel submenu body inline while retained Tools execution and options wiring remain.App::update()now delegates retained app-frame layout update and canvas-toolbar refresh execution throughsrc/legacy_app_frame_services.*, sosrc/legacy_app_runtime_shell_services.cppis thinner at the frame update seam while draw-time execution still remains there.App::tick()andApp::resize()now delegate retained app-frame tick and surface-resize execution throughsrc/legacy_app_frame_services.*, sosrc/app_events.cppis thinner at the frame-execution seam while broader input/platform dispatch still remains there.App::ui_save()andApp::ui_restore()now delegate retained floating and docked panel persistence throughsrc/legacy_app_ui_state_services.*, sosrc/app_layout_ui_state.cppis down to thin preference adapters while RTL direction execution stays local.App::init_sidebar()now delegates the retained color-popup open/close wiring throughsrc/legacy_sidebar_color_popup_services.*with explicitApp&, popup-root, trigger-button, and panel dependencies, sosrc/app_layout_sidebar.cppis thinner while the retained stroke/grid/layer popup families still remain inline.App::dialog_usermanual(),App::dialog_changelog(), andApp::dialog_about()now delegate the retained info-dialog construction and overlay close wiring throughsrc/legacy_info_dialog_services.*with explicitApp&plus overlay-anchor dependencies, sosrc/app_dialogs_info_openers.cppis thinner whileopen_whatsnew_dialog()still owns the retained whats-new flow inline.App::dialog_newdoc()andApp::dialog_save()now delegate retained dialog construction and button wiring throughsrc/legacy_document_session_services.*, sosrc/app_dialogs_workflow.cppis thinner at the document-session seam while browse, open, and resize retained workflows still remain there.App::title_update()now delegates retained document-title and DPI-label rendering throughsrc/legacy_app_status_services.*, sosrc/app_layout.cppno longer owns that app-status family inline.App::init_toolbar_draw()now delegates retained draw-toolbar button lookup, click wiring, and default-tool application throughsrc/legacy_draw_toolbar_binding_services.*, sosrc/app_layout_draw_toolbar.cppis down to a thin adapter while retained tool execution still flows throughsrc/legacy_canvas_tool_services.*.App::init_sidebar()now delegates the retained stroke-popup open/anchor/tick wiring throughsrc/legacy_sidebar_stroke_popup_services.*with explicitApp&, popup-root, trigger-button, and panel dependencies, sosrc/app_layout_sidebar.cppis thinner while the retained grid and layer popup families still remain inline.
Parallel Assignment Rules
Coordinator packets for workers should include only:
- task id and one-paragraph goal
- exact write scope
- allowed read scope
- debt ids that matter
- required validation command
- specific
rgorclangd_nav.pyqueries - current behavior notes needed to avoid broad rediscovery
Safe parallel groups:
- One worker on canvas/render execution, one worker on generic UI controls, one worker on platform CMake cleanup, and one worker on runtime queue contracts can run in parallel if write scopes remain disjoint.
- Do not run two workers against
src/app_runtime.*,src/node.*,src/legacy_canvas_document_io_services.cpp, orsrc/legacy_node_stroke_preview_runtime_services.cppat the same time. - Do not assign both a CMake source-list move and code edits touching the same source files to separate workers unless the coordinator serializes the CMake integration.
P0 Queue
ARC-RUN-010 - Harden AppRuntime Into An Explicit Runtime Service
Status: Ready
Why now:
Render/UI/background queues are central to memory and thread safety. The
current AppRuntime owns several std::jthread workers, but runtime state is
still mutable, partly unsynchronized, and app-specific. The working app still
uses App::I as the practical access path to render/UI queues.
Write scope:
src/app_runtime.hsrc/app_runtime.cppsrc/app.hsrc/legacy_app_runtime_shell_services.cppsrc/app_core/app_thread.htests/app_core/app_thread_tests.cpp
Read scope:
src/texture.cppsrc/rtt.cppsrc/shape.cppsrc/shader.cppsrc/platform_windows/windows_platform_services.cpp
Required work:
- Make render/UI/prepared-file/canvas worker running state synchronized or atomic, with a single shutdown path per worker.
- Add explicit runtime service methods for thread-affinity checks and post/drain/shutdown semantics.
- Keep exceptions from escaping worker bodies.
- Stop exposing queue usage only through
App::Iwrappers for touched call sites. - Keep behavior identical for same-thread immediate execution and blocking render/UI calls.
Done when:
- Touched queue state has no unsynchronized read/write ambiguity.
- Worker shutdown drains or rejects queued work according to documented behavior.
- Touched app code can call an explicit runtime service instead of reaching queues through singleton state.
- App-thread planner tests cover shutdown, stopped-worker enqueue, same-thread execution, and queue-drain behavior that the live runtime implements.
Validation:
powershell -ExecutionPolicy Bypass -File scripts\automation\quiet-validate.ps1 -BuildTargets PanoPainter,pp_app_core_app_thread_tests -TestRegex "pp_app_core_app_thread"
python scripts/dev/check_component_boundaries.py
Mini-model packet:
Start by auditing render_running_, ui_running_,
prepared_file_running_, canvas_async_running_, thread ids, and worker stop
methods. Do not rewrite GL resources in this task; expose the runtime contract
needed for the next task.
ARC-RND-010 - Move GL Resource Queueing Behind Renderer Runtime Contracts
Status: Ready
Why now:
RTT, Texture2D, Shape, Shader, Font, and CanvasLayer still use
App::I->render_task* directly. That blocks renderer backends and hides thread
affinity behind a global app singleton.
Write scope:
src/texture.cppsrc/texture.hsrc/rtt.cppsrc/rtt.hsrc/shape.cppsrc/shape.hsrc/shader.cppsrc/shader.hsrc/font.cppsrc/font.hsrc/canvas_layer.cppsrc/canvas_layer.h- narrow adapter files if introduced under
src/renderer_gl/
Read scope:
src/app_runtime.*src/renderer_api/*src/renderer_gl/*src/paint_renderer/*
Required work:
- Introduce a narrow render-dispatch interface or adapter consumed by retained GL resource classes.
- Convert one coherent GL resource family per slice; do not edit every file in one worker pass unless the abstraction is already integrated.
- Preserve blocking versus async semantics exactly.
- Do not move app policy into
pp_renderer_gl. - Do not add future backend implementation work.
Done when:
- The touched GL resource family no longer calls
App::Idirectly for queueing or thread checks. - Render-thread assertions use an explicit runtime/render-dispatch contract.
- CMake ownership remains consistent and no pure renderer API target depends on app headers.
Validation:
powershell -ExecutionPolicy Bypass -File scripts\automation\quiet-validate.ps1 -BuildTargets PanoPainter,pp_renderer_api_tests,pp_renderer_gl_capabilities_tests -TestRegex "pp_renderer|pp_paint_renderer"
python scripts/dev/check_renderer_api_contract.py
Mini-model packet:
Start with either Texture2D/RTT or Shape/Shader, not both. Use rg -n "App::I->render_task|is_render_thread" src/texture.* src/rtt.* for the first
slice.
ARC-RND-011 - Split Canvas Document I/O From Render Execution
Status: Ready
Why now:
src/legacy_canvas_document_io_services.cpp is still the largest working-app
document/export hotspot and has the highest App::I concentration found in the
review. It mixes license checks, worker dispatch, render readback, progress UI,
platform publish/flush, and retained Canvas mutation.
Write scope:
src/legacy_canvas_document_io_services.cppsrc/legacy_canvas_document_io_services.hsrc/legacy_document_export_services.*src/app_core/document_export.hsrc/paint_renderer/*- focused tests under
tests/app_coreortests/paint_renderer
Read scope:
src/canvas.*src/canvas_layer.*src/legacy_canvas_render_shell_services.*src/platform_api/platform_services.h
Required work:
- Pick one export/import family first: equirectangular export, cube-face export, layer/frame collection export, or project save/open async I/O.
- Move orchestration into an app-core or paint-renderer service request that accepts explicit document/render/platform dependencies.
- Leave retained
Canvasas a final adapter only for data that has not moved. - Remove direct
App::Icalls from the touched path. - Preserve progress and platform publish behavior.
Done when:
- One live document/export path is executable through an explicit service
request rather than by walking
App::I/Canvas::Ifrom the bridge. - The retained bridge is visibly thinner and has fewer reasons to know about UI, worker, platform, and renderer details at the same time.
- The touched path has focused validation that exercises the new request contract and the retained adapter.
Validation:
powershell -ExecutionPolicy Bypass -File scripts\automation\quiet-validate.ps1 -BuildTargets PanoPainter,pano_cli,pp_app_core_document_export_tests,pp_paint_renderer_compositor_tests -TestRegex "document_export|paint_renderer"
Mini-model packet: Do not broaden into all export types. Start with the path that already has the strongest pure planning/readiness coverage, then remove only the corresponding direct app/canvas singleton reach.
ARC-RND-012 - Make Stroke Preview A Renderer-Owned Service
Status: Ready
Why now:
NodeStrokePreview has been thinned, but
src/legacy_node_stroke_preview_runtime_services.cpp still owns static worker
state, render-context handoff, preview texture lifetime, and direct app/canvas
access. This is a high-risk UI/render/thread boundary.
Write scope:
src/node_stroke_preview.*src/legacy_node_stroke_preview_runtime_services.*src/legacy_node_stroke_preview_draw_services.*src/legacy_node_stroke_preview_sample_services.*src/paint_renderer/*tests/paint_renderer/*
Read scope:
src/app_runtime.*src/texture.*src/rtt.*src/canvas.*src/node_panel_stroke.*
Required work:
- Move one preview execution phase behind a renderer-facing service contract.
- Replace static worker/resource state for the touched phase with owned service state or explicit runtime dependency.
- Remove direct
App::I/Canvas::Ifrom the touched phase. - Preserve preview output and cancellation/shutdown behavior.
Done when:
- The touched preview phase can be reasoned about without reading the full node implementation.
- Preview worker lifetime is owned and cancellable for the touched phase.
- Renderer-facing tests cover the contract without linking app texture objects.
Validation:
powershell -ExecutionPolicy Bypass -File scripts\automation\quiet-validate.ps1 -BuildTargets PanoPainter,pp_paint_renderer_compositor_tests -TestRegex "paint_renderer|stroke_preview"
Mini-model packet: Start with result copy, live pass request assembly, or worker lifecycle. Do not combine all preview phases in one slice.
ARC-UI-010 - Move Generic Controls Out Of pp_legacy_ui_core
Status: Ready
Why now:
Generic controls still live in PP_LEGACY_UI_CORE_SOURCES, keeping
pp_panopainter_ui tied to retained app/UI targets. This is working-app UI
architecture, not cosmetic cleanup.
Write scope:
src/node_button.*src/node_checkbox.*src/node_icon.*src/node_image.*src/node_scroll.*src/node_slider.*src/node_text.*src/node_text_input.*src/ui_core/*cmake/PanoPainterSources.cmakeCMakeLists.txt
Read scope:
src/node.*src/layout.*- app-specific
src/node_panel_* - app-specific
src/node_dialog_*
Required work:
- Move one generic control family at a time to
pp_ui_core. - Split renderer-neutral state/event/layout logic from retained GL drawing when a control still depends on GL classes.
- Keep app-specific panels and dialogs out of
pp_ui_core. - Update CMake ownership so the source-list change is real.
Done when:
- At least one generic control family is owned by
pp_ui_coreor has its renderer-neutral core owned there with only a narrow retained draw adapter. PP_LEGACY_UI_CORE_SOURCESshrinks.- Existing UI behavior is unchanged.
Validation:
powershell -ExecutionPolicy Bypass -File scripts\automation\quiet-validate.ps1 -BuildTargets PanoPainter,pp_ui_core_layout_xml_tests,pp_ui_core_node_lifetime_tests,pp_ui_core_overlay_lifetime_tests -TestRegex "pp_ui_core"
python scripts/dev/check_component_boundaries.py
Mini-model packet: Start with the least app-specific control: checkbox, button, icon, image, scroll, slider, text, or text input. Do not touch panels/dialogs in the same slice.
ARC-UI-011 - Convert UI Ownership To Checked Handles By Default
Status: Ready
Why now:
pp_ui_core has checked lifetime helpers, but base Node and app panels still
mix raw parent/manager pointers, shared child vectors, raw callback parameters,
and destroy-during-callback assumptions.
Write scope:
src/node.*src/layout.*src/legacy_ui_overlay_services.*- one dialog or panel family per slice under
src/node_dialog_*orsrc/node_panel_* src/ui_core/node_lifetime.*src/ui_core/overlay_lifetime.*
Read scope:
- call sites found with
rg -n "add_child|remove_child|destroy\\(|on_.*=|Node\\*" src/node_dialog_* src/node_panel_* src/legacy_ui_* src/node.*
Required work:
- Convert one popup/dialog/panel family to checked handles or scoped connections.
- Remove raw lifetime assumptions from callbacks in the touched family.
- Document any remaining raw
Node*as non-owning views with owner proof. - Keep visual behavior and event ordering unchanged.
Done when:
- The touched UI family can close during callback dispatch without relying on dangling raw pointers.
- Overlay/popup lifetime flows through
pp_ui_corelifetime primitives by default. - New touched callbacks are scoped or handle-checked.
Validation:
powershell -ExecutionPolicy Bypass -File scripts\automation\quiet-validate.ps1 -BuildTargets PanoPainter,pp_ui_core_node_lifetime_tests,pp_ui_core_overlay_lifetime_tests -TestRegex "ui_core_(node_lifetime|overlay_lifetime)"
Mini-model packet:
Pick one family only, such as open/browse dialogs, picker dialog, popup menu,
layer panel, or stroke panel. Avoid broad Node redesign unless the family
requires a small base helper.
ARC-APP-010 - Reduce App Shells To Composition And Adapters
Status: Ready
Why now:
PP_PANOPAINTER_APP_SOURCES is still about 9620 lines. The app shell owns
workflow, dialogs, layout binding, runtime, VR, cloud, brush package, platform
hooks, and retained document/export adapters.
Write scope:
- one
src/app_*.cppfamily per slice - matching
src/legacy_app_*service files - matching app-core planner/service headers only when needed
cmake/PanoPainterSources.cmakeif ownership moves
Read scope:
src/app.h- relevant app-core headers under
src/app_core - relevant UI node files for the touched workflow
Required work:
- Pick one shell family: layout menus, dialogs, startup/frame, cloud, brush package, recording, VR, or document session.
- Move retained implementation into a named service with explicit dependencies.
- Make the app method a thin adapter or composition call.
- Do not add planner-only coverage unless the app method actually shrinks.
Done when:
- One app shell file loses real workflow/runtime ownership.
- The new service accepts explicit
App&, runtime, platform, UI, document, or renderer dependencies instead of reading global state internally. - The touched path has focused validation or an app build gate.
Validation:
powershell -ExecutionPolicy Bypass -File scripts\automation\quiet-validate.ps1 -BuildTargets PanoPainter,pano_cli -TestRegex "pp_app_core|pano_cli_plan"
Mini-model packet: Start with a single app shell family. Do not mix dialogs, layout, cloud, and VR in one worker assignment.
ARC-PLT-010 - Finish Platform Source Ownership In CMake
Status: Ready
Why now:
Platform implementation ownership improved, but CMake still leaks Web platform
sources into PP_PANOPAINTER_APP_SOURCES. Platform implementation files should
belong to concrete pp_platform_* targets, not the app source group.
Write scope:
cmake/PanoPainterSources.cmakeCMakeLists.txtsrc/platform_web/*only if build integration requires a narrow include or factory adjustment
Read scope:
webgl/CMakeLists.txtsrc/platform_android/*src/platform_linux/*src/platform_apple/*src/platform_api/platform_services.h
Required work:
- Remove
${PP_PLATFORM_WEB_SOURCES}fromPP_PANOPAINTER_APP_SOURCES. - Ensure root app/platform targets link
pp_platform_webonly when needed. - Keep WebGL retained package entrypoint behavior unchanged.
- Do not reintroduce
platform_legacy.
Done when:
- Concrete Web platform files are not compiled as app sources in the root app source group.
- The source ownership direction is visible in CMake.
- Platform API boundary checks still pass.
Validation:
powershell -ExecutionPolicy Bypass -File scripts\automation\quiet-validate.ps1 -BuildTargets PanoPainter,pp_platform_api_tests -TestRegex "pp_platform_api"
python scripts/dev/check_component_boundaries.py
Mini-model packet: Keep this structural. Do not edit platform behavior unless CMake exposes a real link or include problem.
P1 Queue
ARC-SAFE-010 - Remove Manual Allocation From Touched Ownership Paths
Status: Ready
Why now:
The review found manual new/delete pockets in node loading, canvas, layer
actions, Wacom/bootstrap helpers, and retained resource cleanup. Some are
non-owning or placement-new cases, but touched working-app ownership paths
should move to RAII containers and factories.
Write scope:
- one selected ownership path at a time, such as
src/legacy_ui_node_loader.*,src/node.*,src/canvas.cpp,src/platform_windows/windows_bootstrap_helpers.cpp, orsrc/wacom.cpp
Read scope:
- immediate owner/caller files for the selected path
Required work:
- Replace owning raw allocation with
std::unique_ptr,std::shared_ptr,std::vector,std::string, or an explicit RAII wrapper. - Preserve non-owning views only where ownership is proven.
- Avoid mixing this with UI or renderer redesign.
Done when:
- The touched path has no owning raw
new/delete. - Failure paths cannot leak.
- Lifetime remains clear at call sites.
Validation:
powershell -ExecutionPolicy Bypass -File scripts\automation\quiet-validate.ps1 -BuildTargets PanoPainter -TestRegex "pp_ui_core|pp_app_core"
Mini-model packet:
Start with a single obvious allocation family. legacy_ui_node_loader is a
good first target because it is UI ownership, not rendering behavior.
ARC-SAFE-011 - Replace Remaining Ad Hoc Workers With Runtime-Owned Services
Status: Ready
Why now:
Most recent worker conversions use std::jthread, but retained worker pockets
still sit in UI/dialog/cloud/grid/preview services and std::async remains in
Asset. The end state requires service-owned cancellation and shutdown.
Write scope:
- one worker family per slice:
src/node_dialog_cloud.*,src/legacy_cloud_services.*,src/legacy_grid_ui_services.*,src/asset.*, orsrc/legacy_node_stroke_preview_runtime_services.*
Read scope:
src/app_runtime.*- corresponding app-core/cloud/grid/preview planner headers
- immediate UI caller files
Required work:
- Move worker ownership behind a runtime/service contract.
- Add cancellation or shutdown semantics for the touched worker.
- Avoid capturing raw nodes across worker completion without checked handles.
- Preserve progress and completion callbacks.
Done when:
- The touched worker cannot outlive its owner.
- Shutdown behavior is explicit and validated.
- UI completion handoff is handle-safe or owner-checked.
Validation:
powershell -ExecutionPolicy Bypass -File scripts\automation\quiet-validate.ps1 -BuildTargets PanoPainter -TestRegex "pp_app_core|pp_ui_core"
Mini-model packet: Pick one worker family. Do not perform broad thread cleanup across unrelated subsystems in one task.
ARC-WKF-010 - Thin Document Session/Open/Save Bridges
Status: Ready
Why now:
The pure document/session planners are extensive, but live bridges still own
retained prompts, metadata mutation, title updates, history clearing, snapshot
handoff, and legacy Canvas execution.
Write scope:
src/legacy_document_open_services.*src/legacy_document_session_services.*src/legacy_history_services.*- focused app-core document/session headers only when needed
Read scope:
src/app_core/document_route.hsrc/app_core/document_session.hsrc/app_core/document_canvas.hsrc/app.hsrc/canvas.*
Required work:
- Pick one bridge path: open-project confirmation, save-before-workflow, save-version, new-document overwrite, history clear, or title update.
- Move decisions/mutations behind explicit service requests.
- Keep retained prompts as adapters only.
Done when:
- One document workflow path has a single obvious owner for decision, execution, and metadata mutation.
- The retained bridge has less direct
App::I/Canvas::Ireach. - Behavior is covered by existing or focused document-session validation.
Validation:
powershell -ExecutionPolicy Bypass -File scripts\automation\quiet-validate.ps1 -BuildTargets PanoPainter,pano_cli,pp_app_core_document_session_tests,pp_app_core_document_route_tests -TestRegex "document_(session|route)"
Mini-model packet: Do not rewrite all document flows. Pick the narrowest path that removes live bridge ownership.
ARC-WKF-011 - Split Cloud And Brush Package Work Out Of UI Nodes
Status: Ready
Why now: Cloud browse/download/upload and brush package import/export still mix UI node lifetime, worker ownership, storage, network/asset behavior, and app singleton reach.
Write scope:
- one family per slice:
src/legacy_cloud_services.*,src/node_dialog_cloud.*,src/legacy_brush_package_import_services.*,src/legacy_brush_package_export_services.*,src/legacy_brush_preset_services.*,src/node_panel_brush.cpp
Read scope:
src/app_core/document_cloud.hsrc/app_core/brush_package_import.hsrc/app_core/brush_package_export.hsrc/assets/brush_package.*- relevant panel/dialog headers
Required work:
- Separate worker/network/asset execution from node lifetime.
- Use app-core requests and asset helpers where they already exist.
- Use checked handles for UI completion callbacks.
- Preserve current cloud and brush package UX.
Done when:
- One cloud or brush package path can be understood without reading panel or dialog internals first.
- The touched UI node is a view/controller shell, not the workflow owner.
- The touched worker cannot outlive its service/UI owner.
Validation:
powershell -ExecutionPolicy Bypass -File scripts\automation\quiet-validate.ps1 -BuildTargets PanoPainter,pano_cli,pp_app_core_document_cloud_tests,pp_app_core_brush_package_import_tests,pp_app_core_brush_package_export_tests,pp_assets_brush_package_tests -TestRegex "document_cloud|brush_package"
Mini-model packet: Cloud and brush package work are separate packets. Do not assign both to one small worker.
Deferred On Purpose
- Vulkan, Metal, WebGPU, and broad future-backend implementation.
- OpenXR implementation beyond boundary cleanup needed to remove OpenVR debt.
- Package-only migration that does not affect root app architecture.
- CLI/planner-only expansion.
- Broad warning cleanup without ownership movement.
- Documentation-only progress claims.