Files
panopainter/docs/modernization/debt.md

118 KiB

Modernization Debt Log

Status: live Last updated: 2026-06-05

Every shortcut, temporary adapter, retained vendored dependency, skipped platform gate, compatibility shim, or incomplete automation path must be recorded here before it lands. Entries must be specific enough for a future agent or engineer to remove them without reconstructing context from chat.

Entry Rules

  • Add an entry before merging the shortcut.
  • Reference the debt id in code comments, TODOs, ADRs, or roadmap notes.
  • Include an owner, reason, validation command, and removal condition.
  • Do not close an entry until the removal condition is met and validated.
  • Prefer deleting shortcuts over expanding this log.

Recent Reductions

  • 2026-06-04: DEBT-0009 was narrowed. platform-build.ps1 and platform-build.sh now include the current headless component/test matrix, including brush-package coverage and the app-core startup/frame/shutdown/file/document/ brush/canvas/history/grid/toolbar/tools/about/preferences/status automation tests, and panopainter_platform_build_target_matrix_self_test now verifies the wrapper defaults against the current CMake test executables. On 2026-06-05 the default platform-build preset set was widened to Android standard arm64/x64, Quest arm64, and Focus/Wave arm64, with the shell wrapper gaining multi-preset output parity with PowerShell, so Android root CMake validation no longer silently skips newly extracted feature slices or named Android variants. Package targets remain open under DEBT-0009 and DEBT-0011.
  • 2026-06-05: DEBT-0011 was narrowed. package-smoke.ps1 and package-smoke.sh now have readiness-only modes that report the same Windows AppX, Android standard/Quest/Focus APK, Apple bundle, Linux app, and WebGL blocker matrix without requiring an app build first, and panopainter_package_smoke_readiness_self_test guards package-kind parity across both wrappers. Package target migration remains open.
  • 2026-06-05: DEBT-0004 was narrowed for Apple compile coverage. The Mac mini build host panopainter-mac now pulls from Gitea over SSH, uses Homebrew CMake/Ninja/Git plus full Xcode through DEVELOPER_DIR, and scripts/automation/apple-remote-build.ps1 drives the macos, ios-simulator, and ios-device root CMake headless component matrix. Apple app bundle/signing/package migration remains open under DEBT-0011 and DEBT-0059.
  • 2026-06-05: DEBT-0022 was narrowed. pp_app_core now owns tested onion-skin frame range and alpha falloff planning, and live NodeCanvas panorama drawing consumes that helper instead of open-coding frame clamping and opacity falloff in the render loop. Later on 2026-06-05, animation timeline mouse scrubbing also moved to tested pp_app_core planning with pano_cli plan-animation-timeline-scrub coverage, so NodeAnimationTimeline no longer owns cursor-to-frame clamp policy. Animation panel layer/frame view projection now also uses a tested pp_app_core view model exposed by pano_cli plan-animation-panel-view, including stale-selection behavior. Legacy canvas/layer/UI execution remains open under DEBT-0022.
  • 2026-06-05: DEBT-0047 was narrowed. PPBR export success-dialog metadata now lives in tested pp_app_core, is exposed through pano_cli plan-brush-package-export, and is consumed by src/legacy_brush_package_export_services.*. Retained NodeDialogExportPPBR reads, legacy Image header ownership, PPBRInfo conversion, NodePanelBrushPreset::export_ppbr, desktop worker threading, dialog lifetime, mobile/Web completion, and PPBR serialization remain open.
  • 2026-06-05: DEBT-0021 was narrowed again. Layer panel selected-control and visibility view projection now goes through tested pp_app_core planning, NodePanelLayer::update_attributes() consumes that view model in the live app, and pano_cli plan-layer-panel-view exposes the same path for automation. Legacy layer mutation, UI node ownership, and undo wiring remain open under DEBT-0021.
  • 2026-06-05: DEBT-0023 was narrowed again. Stroke-panel control projection now goes through tested pp_app_core planning, NodePanelStroke::update_controls() consumes that view model in the live app, and pano_cli plan-brush-stroke-panel-view exposes the same state path for automation. Legacy brush mutation, brush thumbnail ownership, popup behavior, and preset child-node mutation remain open under DEBT-0023.
  • 2026-06-05: DEBT-0023 was narrowed again. App-level brush refresh projection now goes through tested pp_app_core planning, App::brush_update() consumes that view model in the live app, and pano_cli plan-brush-refresh exposes the same fan-out path for automation. Retained legacy quick/stroke/color widget writes plus Brush/Canvas::I ownership remain open under DEBT-0023.
  • 2026-06-05: DEBT-0025 was narrowed. Quick size/flow slider preview planning now goes through tested pp_app_core, live NodePanelQuick slider callbacks consume that plan for cursor placement and pen/line tip flags, and pano_cli plan-quick-slider-preview exposes the path for automation. Legacy quick widgets, brush previews, popup state, and direct CanvasMode* field writes remain open under DEBT-0025.
  • 2026-06-05: DEBT-0027 was narrowed. Canvas cursor visibility policy now goes through tested pp_app_core, live NodeCanvas::update_cursor() consumes the planner before retained platform cursor dispatch, and pano_cli plan-canvas-cursor exposes draw/erase versus non-paint mode, small-brush, not-painting, modifier, and malformed-brush states for automation. Legacy Canvas/CanvasModePen state reads and app cursor execution remain open under DEBT-0027.
  • 2026-06-05: DEBT-0027 was narrowed again. The full draw-toolbar binding set now goes through tested pp_app_core planning, live App::init_toolbar_draw() consumes the same plan for button handler wiring and default draw-mode initialization, and pano_cli plan-canvas-tool-toolbar exposes the binding ids, actions, button-class expectation, and default selection for automation. Retained NodeButton/NodeButtonCustom lookup, legacy Canvas mode mutation, picking/touch-lock state, and transform action execution remain open under DEBT-0027.
  • 2026-06-05: DEBT-0033 was narrowed again. Canvas reset-camera defaults, viewport density, and cursor mode now go through tested pp_app_core plans and shared src/legacy_canvas_view_services.* execution. Live Tools reset-camera, document open/new-document reset, cloud download reset, and options viewport/cursor callbacks consume that bridge, while pano_cli plan-canvas-camera-reset, pano_cli plan-canvas-view-density, and pano_cli plan-canvas-view-cursor-mode expose the paths for automation. This also narrows DEBT-0045 for viewport-density and cursor-mode preference execution, though preference persistence remains retained in the legacy canvas-view bridge.
  • 2026-06-05: DEBT-0046 was narrowed. Main startup shader, asset, layout, title, and UI render-target sequencing now goes through tested pp_app_core resource plans and src/legacy_app_startup_services.*; App::init keeps the retained OpenGL startup task in place, then delegates startup resources and runtime side effects through the startup bridge. pano_cli plan-app-startup-resources exposes the resource path for automation.
  • 2026-06-05: DEBT-0040/0041/0042 were narrowed. Close-unsaved, save-before-workflow, new-document overwrite, Save As overwrite, and save-error prompt metadata now comes from a tested pure document-session prompt catalog consumed by src/legacy_document_session_services.*, and pano_cli plan-document-session-prompt exposes the titles, messages, captions, and cancel visibility for automation. Retained NodeMessageBox creation, callback wiring, project-save execution, and canvas/document mutation remain open under the same debts.
  • 2026-06-05: DEBT-0040/0041/0042 were narrowed again. Close-unsaved, save-before-workflow, new-document overwrite, and Save As overwrite prompt creation now uses src/legacy_app_dialog_services.*; retained document-session callbacks, save dialogs, canvas/project mutations, keyboard cleanup, and dialog lifetime still remain open under the same debts.
  • 2026-06-05: DEBT-0003 was narrowed. Initial surface sizing, redraw/animation update gating, layout tick selection, resize render-target recreation, canvas-stroke draw eligibility, VR UI pass selection, main UI pass selection, UI observer clipping/on-screen transition/scissor projection, and redraw reset are now tested pp_app_core frame plans consumed by App::create, App::tick, App::resize, App::update, App::draw, App::update_ui_observer, and pano_cli plan-app-frame; retained layout traversal, toolbar widget writes, render-target recreation, Node parent walking, on-screen callback execution, and OpenGL/UI drawing remain in the legacy app.
  • 2026-06-05: DEBT-0003 was narrowed. Pointer coordinate normalization, mouse designer-first routing, gesture midpoint/delta math, touch/key main-layout routing, VR spacebar camera-sync intent, UI visibility toggling, and stylus touch-lock attachment are now tested pp_app_core input plans consumed by App::mouse_*, App::gesture_*, App::touch_tap, App::key_*, App::toggle_ui, App::set_stylus, and pano_cli plan-app-input; retained MouseEvent/GestureEvent/TouchEvent/KeyEvent construction, UI child-node mutation, and legacy Node event dispatch remain in the app shell.
  • 2026-06-05: DEBT-0003 was narrowed. Render/UI task dispatch, unique queued task replacement, async redraw notification, queue draining, render-context wrapping, UI tick redraw scheduling, UI-loop frame/FPS/live-reload timer cadence, and thread start/stop intents are now tested pp_app_core plans consumed by App::render_task*, App::ui_task*, App::async_redraw, App::render_thread_*, App::ui_thread_*, and pano_cli plan-app-thread; retained std::thread, condition-variable, OpenGL context, live asset reload, and task execution remain in the app shell.
  • 2026-06-05: DEBT-0003 was narrowed again. Shutdown cleanup staging for UI-state save, stroke-preview renderer shutdown, recording stop, texture/shader invalidation, layout unload, UI render-target and face-plane destruction, panel-node release, and quick-mode cleanup now goes through tested pp_app_core plans consumed by App::terminate and pano_cli plan-app-shutdown; retained cleanup execution remains in the legacy app.
  • 2026-06-05: DEBT-0003 was narrowed again. Command-line panorama conversion sequencing for renderer-state setup, temporary canvas allocation, project open, and equirectangular export now goes through tested pp_app_core planning consumed by App::cmd_convert and pano_cli plan-command-convert; retained OpenGL state dispatch and legacy Canvas project open/export execution remain in the legacy app.
  • 2026-06-05: DEBT-0003 was narrowed again. Recording worker encode-wake eligibility now goes through tested pp_app_core planning consumed by App::rec_loop and pano_cli plan-recording-session; retained PBO equirect generation, dirty-stroke mutation, MP4 encoder calls, and frame label rendering remain in the legacy app/canvas/video path.
  • 2026-06-05: DEBT-0037 was narrowed. MP4 recording export progress-dialog metadata now lives in tested pp_app_core planning, is exposed by pano_cli plan-recording-session, and retained progress bar creation now routes through src/legacy_app_dialog_services.*. Recording thread lifecycle, frame readback scheduling, progress lifetime, and MP4 writing remain open.
  • 2026-06-05: DEBT-0038 was narrowed. Cloud transfer request/progress policy now lives in tested pp_app_core planning, live App::download and App::upload consume those plans before retained CURL setup, and pano_cli plan-cloud-transfer exposes missing endpoint, TLS policy, progress-callback, and zero/overrun progress cases for automation. CURL ownership, response/error handling, progress UI, cloud dialog/document execution, and injected network service work remain open under DEBT-0038.
  • 2026-06-05: DEBT-0038 was narrowed again. Cloud upload warning/publish/ success prompts, upload/bulk progress dialog titles, download-progress prompt metadata, and formatted download progress text now live in tested pp_app_core plans consumed by src/legacy_cloud_services.*; existing pano_cli plan-cloud-upload, pano_cli plan-cloud-upload-all, and pano_cli plan-cloud-transfer expose that metadata for automation. Retained NodeMessageBox/progress-bar creation, save-before-upload threading, CURL ownership, response/error handling, and downloaded-project execution remain open under DEBT-0038.
  • 2026-06-05: DEBT-0038 was narrowed again. Cloud download progress prompt creation now uses src/legacy_app_dialog_services.*, matching the upload warning/publish/success and bulk progress paths that already route through App::message_box/App::show_progress. Retained cloud prompt/progress lifetime, save-before-upload threading, CURL ownership, response/error handling, and downloaded-project execution remain open.
  • 2026-06-05: DEBT-0035 was narrowed. Main-toolbar test-message dialog metadata now lives in tested pp_app_core planning, is exposed by pano_cli plan-main-toolbar --command message-box, and retained live message-box creation now routes through src/legacy_app_dialog_services.*. Open/save/settings routing, raw compatibility pointers, history/canvas adapter dispatch, and settings dialog execution remain open.
  • 2026-06-05: DEBT-0058 was opened. App-level progress, message, and input dialog metadata now lives in tested pp_app_core planning consumed by App::show_progress, App::message_box, App::input_box, and pano_cli plan-app-dialog, but retained Node* dialog creation, layout insertion, callback wiring, and lifetime ownership remain in the legacy app. Later on 2026-06-05, retained NodeProgressBar, NodeMessageBox, and NodeInputBox creation moved into src/legacy_app_dialog_services.*, making the App methods thin adapters while retained layout/lifetime ownership remains open.
  • 2026-06-05: DEBT-0043 and DEBT-0044 were narrowed. Export success, failure, and license-disabled dialog metadata plus export execution log labels now live in tested pp_app_core planning consumed by src/legacy_document_export_services.*, App::dialog_export*, pano_cli plan-export-message, and pano_cli plan-export-report. Retained canvas/video export calls, Web prepared-file handoff, picker execution, export-directory creation, and desktop timelapse worker threading remain open.
  • 2026-06-05: DEBT-0050, DEBT-0051, DEBT-0052, DEBT-0053, and DEBT-0057 were narrowed. pp_platform_api now owns a tested platform_policy catalog for retained platform-family decisions covering iOS exported-image publishing, WebGL persistent-storage flushing, iOS document browse Inbox roots, Windows/macOS working-directory picker availability, iOS/Web prepared-file target selection, iOS collection-export working-directory policy, macOS PPBR data directory override, SonarPen availability, WebGL default canvas resolution, native UI/window state saving, live asset reload policy, recording cleanup policy, and canvas tip visibility. WindowsPlatformServices and the non-Windows legacy fallback consume those helpers while SDK-specific execution remains open in platform shells.
  • 2026-06-05: DEBT-0054 was narrowed. Layout XML file reload policy now has a pure plan_asset_file_load_with_probe decision in pp_platform_api, with Windows/macOS mtime reload behavior and mobile/Web already-loaded no-op behavior selected by the tested platform_policy catalog. The live plan_asset_file_load wrapper still performs the retained stat probe for mtime platforms until asset/file watching is owned by injected storage services.
  • 2026-06-05: DEBT-0056 was narrowed. src/asset.h no longer exposes Android SDK types or forward declarations; retained Android asset-manager and asset handles are stored as opaque pointers and cast only inside src/asset.cpp, where the concrete <android/asset_manager.h> dependency remains. The legacy Android asset.cpp object compiled under the old Android CMake path; the full legacy native-lib target still fails later on unrelated pre-modernization C++ standard/header issues.
  • 2026-06-05: DEBT-0009/0011 were narrowed for retained Android packages. Standard/Quest/Focus package CMake files now use CMake 3.10, target C++23, share the current modern component/service source set, and route nanort through a generated compatibility overlay without dirtying the submodule. The standard arm64 native-lib package target builds and links directly; Quest and Focus package CMake paths configure with the current Yoga source list. APK/package migration into root CMake remains open.
  • 2026-06-05: DEBT-0009 was narrowed again. Android automation now queries sdkmanager for the newest available NDK/CMake packages, installs newer or missing versions when needed, selects the resulting pair through scripts/automation/android-sdk-env.*, and reports package update decisions in structured wrapper output. The current host reports NDK 30.0.14904198 and CMake 4.1.2 as already-latest-available; root Android pp_assets, retained standard native-lib, and retained Quest/Focus configure gates pass with that pair.
  • 2026-06-05: DEBT-0004 was narrowed. Retained Linux and WebGL app CMake files now require CMake 3.10 and use target-level cxx_std_23 with extensions off instead of global C++14 flags. panopainter_retained_platform_cmake_self_test guards those baselines while root CMake app/package target migration remains open.
  • 2026-06-05: DEBT-0011 was narrowed again. Package readiness now includes a Linux app output blocker and points WebGL readiness at the retained WebGL app CMake entrypoint, with both PowerShell and shell wrappers reporting the retained Linux/WebGL CMake baseline prerequisite guarded by panopainter_retained_platform_cmake_self_test.
  • 2026-06-05: DEBT-0011 was narrowed again. Root CMake now exposes panopainter_linux_webgl_package_readiness, a filtered package-smoke target for the retained Linux app and WebGL readiness blockers.
  • 2026-06-05: DEBT-0011 was narrowed again. package-smoke.ps1 now has -AndroidNativeChecks, which invokes the retained Android standard native-lib build and Quest/Focus configure helper for selected Android package kinds and reports those structured results beside the still-blocked APK readiness matrix. The shell readiness wrapper now advertises the same retained-native validation commands, and the package-smoke self-test guards that metadata.
  • 2026-06-05: DEBT-0011 was narrowed again. Root CMake now exposes non-default package validation targets for package readiness and retained Android native package checks. panopainter_android_native_package_smoke calls the package-smoke Android native gate from CMake, validates the current latest SDK-managed NDK/CMake pair, and still reports APK outputs as blocked until root CMake owns real package targets.
  • 2026-06-05: DEBT-0009 was narrowed again. Root CMake now exposes non-default platform-build automation targets for the default headless sweep, Android root CMake asset validation across standard/Quest/Focus presets, and remote Apple compile validation. The Android target validated pp_assets across Android arm64/x64, Quest arm64, and Focus/Wave arm64 with the latest SDK-managed NDK/CMake pair. The platform-build wrapper defaults also include pp_app_core_app_dialog_tests again, so the target-matrix self-test is green.
  • 2026-06-05: DEBT-0011 was narrowed again. Root CMake now exposes panopainter_windows_app_package_smoke, which calls the full Windows package-smoke command from CMake so the app executable/runtime payload check and Windows AppX blocker matrix are available from the CMake target graph.
  • 2026-06-05: DEBT-0007 was narrowed. platform-build.ps1 now resolves VCPKG_ROOT for vcpkg presets from the environment or bundled Visual Studio installs, reports the selected vcpkg root in JSON, and root CMake exposes panopainter_platform_build_vcpkg_ui_core to validate the vcpkg-backed pp_ui_core/tinyxml2 boundary through pp_ui_core_layout_xml_tests.
  • 2026-06-04: DEBT-0036 was narrowed again. Canvas stroke commit, thumbnail, and object-draw history paths now query saved blend state through tested pp_renderer_gl capability-state dispatch; CanvasLayer equirect export now binds cube textures through tested pp_renderer_gl dispatch; and Font text drawing now activates texture units through tested pp_renderer_gl dispatch. Desktop VR drawing also now routes active texture unit switches and fallback 2D texture unbinds through tested pp_renderer_gl dispatch. The debt remains open for live stroke rasterization, dual-brush compositing, pattern feedback math, thumbnail layer compositing, brush-preview compositing, and retained ShaderManager::ext_* compatibility fields.
  • 2026-06-05: DEBT-0036 was narrowed again. Retained NodeCanvas and NodeStrokePreview draw-state paths now share legacy_ui_gl_dispatch for active-texture selection, fallback 2D texture unbinds, viewport/scissor execution, viewport and clear-color queries, clear-color restore, color-buffer clears, and capability query/apply adapter endpoints. Their live panorama and brush-preview compositing order remains legacy OpenGL and stays open under DEBT-0036.
  • 2026-06-05: DEBT-0036 was narrowed again. Retained CanvasMode overlay, mask, transform, and canvas-tip pick paths now share legacy_ui_gl_dispatch for active-texture selection, capability query/apply, viewport execution, read-framebuffer queries, and RGBA8 pixel-readback adapter endpoints. Mode behavior, transform/cut execution, and live OpenGL paint UI drawing remain retained under DEBT-0036.
  • 2026-06-05: DEBT-0036 was narrowed again. Retained NodePanelGrid heightmap draw and bake setup now share legacy_ui_gl_dispatch for active-texture selection, depth/blend capability query/apply, viewport query/execution, depth clears, and color-write-mask adapter endpoints. Grid lightmap baking, heightmap rendering, and retained OpenGL mesh/texture execution remain open.
  • 2026-06-05: DEBT-0036 was narrowed again. Retained CanvasLayer cube/equirect generation and frame-clear paths now share legacy_ui_gl_dispatch for active-texture selection, cube texture binding, viewport execution, blend capability execution, clear-color query/restore, and color-buffer clear adapter endpoints. The cube-face framebuffer-to-texture copy remained open until it could use the shared retained framebuffer-copy utility bridge.
  • 2026-06-05: DEBT-0036 was narrowed again. CanvasLayer cube-face framebuffer-to-texture copies now use the shared retained copy_framebuffer_to_texture_target utility bridge backed by tested pp_renderer_gl dispatch, so src/canvas_layer.cpp no longer owns a local raw glCopyTexSubImage2D adapter. The bridge remains retained until renderer services own cube and 2D copy execution.
  • 2026-06-05: DEBT-0036 was narrowed again. Retained Canvas stroke, thumbnail, object-render, export, and LayerFrame::clear state endpoints now share legacy_ui_gl_dispatch for active-texture selection, fallback texture unbind, viewport/scissor execution, viewport and clear-color query, clear-color restore, and capability query/apply adapter endpoints. At that checkpoint, Canvas depth renderbuffer allocation/attachment/delete remained a retained renderer-resource bridge.
  • 2026-06-05: DEBT-0036 was narrowed again. Retained Canvas and RTT depth renderbuffer allocation, framebuffer depth attachment, and renderbuffer deletion now share legacy_gl_renderbuffer_dispatch, so those files no longer carry duplicated raw renderbuffer callbacks. Renderer services still need to own renderbuffer lifetime before this bridge can be removed.
  • 2026-06-05: DEBT-0036 was narrowed again. Retained Texture2D, TextureCube, and RTT texture allocation, deletion, binding, parameter setup, 2D update, and mipmap dispatch now share legacy_gl_texture_dispatch, so src/texture.cpp and src/rtt.cpp no longer carry duplicated raw texture callbacks. Renderer services still need to own texture resource lifetime before this bridge can be removed.
  • 2026-06-05: DEBT-0036 was narrowed again. Retained Texture2D readback plus RTT framebuffer allocation, deletion, bind/restore, blit, readback, and PBO readback dispatch now share legacy_gl_framebuffer_dispatch, so src/texture.cpp and src/rtt.cpp no longer carry duplicated raw framebuffer/readback callbacks. Renderer services still need to own framebuffer and readback lifetime before this bridge can be removed.
  • 2026-06-05: DEBT-0036 was narrowed again. Retained Sampler create, parameter, border-color, bind, and unbind dispatch now share legacy_gl_sampler_dispatch, and retained PBO allocation, framebuffer readback, map, unmap, and delete dispatch now share legacy_gl_pixel_buffer_dispatch. Renderer services still need to own sampler and pixel-buffer lifetime before these bridges can be removed.
  • 2026-06-05: DEBT-0036 was narrowed again. Retained Shape, TextMesh, and NodeColorWheel mesh buffer/VAO creation, dynamic vertex/index uploads, fill/stroke/text draws, and buffer/VAO deletion now share legacy_gl_mesh_dispatch, so src/shape.cpp, src/font.cpp, and src/node_colorwheel.cpp no longer carry duplicated raw mesh callback clusters. Renderer services still need to own mesh resource lifetime before this bridge can be removed.
  • 2026-06-05: DEBT-0036 was narrowed again. Retained shader source compilation/deletion, program attach/link/use/delete, attribute rebinding and location lookup, active-uniform enumeration, uniform-location discovery, and vec/mat/scalar uniform writes now share legacy_gl_shader_dispatch, so src/shader.cpp no longer carries raw shader/program/uniform callback ownership. Renderer services still need to own shader-program lifetime before this bridge can be removed.
  • 2026-06-05: DEBT-0036 was narrowed again. Retained gl_state save/restore and copy_framebuffer_to_texture_target now reuse legacy_ui_gl_dispatch, legacy_gl_framebuffer_dispatch, legacy_gl_shader_dispatch, and legacy_gl_sampler_dispatch, so src/util.cpp no longer carries its local raw state/copy callback cluster. Renderer services still need to own state snapshot/restore and framebuffer-copy execution before these bridges can be removed.
  • 2026-06-05: DEBT-0036 was narrowed again. Retained app startup, app clear, app UI viewport/scissor, command-convert renderer state, and desktop VR draw-state endpoints now share legacy_ui_gl_dispatch, so src/app.cpp, src/app_commands.cpp, and src/app_vr.cpp no longer carry duplicated raw callback clusters for capability, blend, clear, viewport, scissor, active texture, or 2D texture-unbind execution. Renderer services still need to own app and VR command execution before these bridges can be removed.
  • 2026-06-05: DEBT-0036 was narrowed again. Retained RTT clear and masked-clear endpoints now share legacy_ui_gl_dispatch, so src/rtt.cpp no longer carries a local raw clear/color-mask callback cluster. Renderer services still need to own RTT render-target execution before this bridge can be removed.
  • 2026-06-05: DEBT-0036 was narrowed again. Retained app startup logging, Windows early context logging/window-title detection, and shader capability detection now share legacy_gl_runtime_dispatch, so src/app.cpp, src/main.cpp, and src/app_shaders.cpp no longer carry duplicated raw runtime-query callback clusters. Renderer services still need to own runtime and capability probing before this bridge can be removed.
  • 2026-06-05: DEBT-0061 was opened. pp_platform_api now owns a tested desktop XR runtime-selection policy that prefers OpenXR and labels OpenVR as a legacy fallback; WindowsPlatformServices consumes that policy before calling the retained OpenVR bridge. The actual OpenXR SDK/backend wiring remains open.
  • 2026-06-05: DEBT-0008 was narrowed. windows-msvc-default and windows-msvc-vcpkg-headless now explicitly select the Visual Studio 18 2026 generator and should be configured with the VS 2026-bundled CMake, avoiding a stale/generated-tree failure where a non-VS2026 CMake selected Ninja while also carrying an x64 platform value.
  • 2026-06-05: DEBT-0062 was opened. VS 2026 builds generate a patched fmt header overlay in the build tree for pp_legacy_vendor so retained fmt skips the removed stdext::checked_array_iterator compatibility path without modifying the fmt submodule.
  • 2026-06-05: DEBT-0061 was narrowed. While OpenVR remains the temporary desktop XR fallback, the Windows runtime payload now deploys openvr_api.dll beside PanoPainter.exe so Visual Studio launches exercise the retained bridge instead of failing during process load.
  • 2026-06-05: DEBT-0011 was narrowed. The Windows app package smoke target now passes the configure-time CMake executable into package-smoke.ps1, so VS 2026 generator validation does not depend on an older cmake on PATH, and the smoke checks cover the Windows launch-folder DLL payload.
  • 2026-06-05: DEBT-0010 was narrowed. pp_paint_renderer now consumes pp_document directly for pure frame/face compositing, expanding per-layer dirty face payload rectangles into a full renderer-sized RGBA buffer while preserving document layer visibility, opacity, blend mode, and uneven per-layer frame timelines.
  • 2026-06-05: DEBT-0010 was narrowed again. The same compositor boundary now exposes a pure six-face document frame composite plus pano_cli simulate-document-render JSON automation, so headless tests can validate document payloads moving toward renderer/export services without a GL context.
  • 2026-06-05: DEBT-0010 was narrowed again. pp_paint_renderer can now upload a pure document frame's six composited faces through the renderer-neutral IRenderDevice texture API, and the recording backend/CLI smoke validate six RGBA8 texture uploads plus explicit shader-read transitions.
  • 2026-06-05: DEBT-0010/DEBT-0036 were narrowed. pano_cli simulate-document-render now also runs those recorded document upload commands through pp_renderer_gl::plan_recorded_render_commands, proving the six texture uploads and transitions are accepted by the current OpenGL command planner while live legacy GL execution remains retained.
  • 2026-06-05: DEBT-0010 was narrowed again. pp_app_core now owns a tested metadata-only live-canvas-to-pp_document::CanvasDocument snapshot planner, pano_cli plan-canvas-document-snapshot exposes the projection as JSON automation, and src/legacy_document_canvas_services.* can build the same snapshot from retained Canvas dimensions, active layer/frame, layer visibility/opacity/alpha/blend metadata, and frame durations. Renderer-owned cube-face pixel payload readback remains open under DEBT-0010/DEBT-0036.
  • 2026-06-05: DEBT-0010 was narrowed again. The canvas snapshot planner now accepts captured RGBA8 face payloads and attaches them to pp_document::CanvasDocument; pano_cli plan-canvas-document-snapshot --captured-face-payloads-per-layer covers payload-bearing automation, and src/legacy_document_canvas_services.* exposes an opt-in dirty-face payload snapshot path backed by retained Layer::snapshot() readback. Live save, export, and action-command adoption of that payload-bearing snapshot remains open under DEBT-0010/DEBT-0013/DEBT-0036.
  • 2026-06-05: DEBT-0010/DEBT-0013/DEBT-0040/DEBT-0042 were narrowed. Live Save, Save As, Save Version, and save-before-workflow now prepare a payload-bearing canvas document snapshot plus pp_app_core save-readiness report before delegating to retained Canvas::project_save. The retained writer still owns PPI serialization, progress/threading, and compatibility quirks, so pure writer replacement remains open.
  • 2026-06-05: DEBT-0010/DEBT-0013 were narrowed again. pp_app_core now exports payload-complete or metadata-only canvas document snapshots through the pure pp_document PPI writer and rejects snapshots that still require renderer readback; pano_cli plan-canvas-document-snapshot reports ppiExport readiness, byte counts, and decoded dirty-face counts.
  • 2026-06-05: DEBT-0010/DEBT-0036 were narrowed again. Payload-complete pano_cli plan-canvas-document-snapshot automation now feeds the captured pp_document snapshot through pp_paint_renderer::upload_document_frame_faces and the renderer-neutral recording backend, reporting texture uploads, transitions, command counts, bytes, and active-frame payload counts.

Open Debt

ID Status Owner Item Reason Validation Removal Condition
DEBT-0001 Open Modernization Existing platform build files remain alongside new CMake Required for incremental migration without losing platform coverage Existing platform builds plus new CMake configure Remove after all platform builds consume shared CMake targets
DEBT-0002 Open Modernization Vendored SDK and patched libraries retained initially, including the generated Android package nanort compatibility overlay tracked by DEBT-0060 and the retained OpenVR SDK fallback tracked by DEBT-0061 Some dependencies are SDK-only, patched, or have platform-specific binaries Dependency inventory and platform build smoke tests Replace with vcpkg packages or document permanent vendored status after triplet evaluation
DEBT-0003 Open Modernization Existing singletons remain during initial split; App::open_document, App::request_close, App::share_file, App::cloud_upload, App::cloud_upload_all, App::cloud_browse, App::rec_start, App::rec_stop, App::rec_clear, App::rec_export, App::rec_loop, App::update_ui_observer, App::render_task*, App::ui_task*, App::render_thread_*, App::ui_thread_*, file-menu save actions, NodeCanvas canvas hotkeys, new/open/browse dirty-document workflow prompts, new-document target/resolution/overwrite decisions, save-as document file naming and overwrite decisions, save-version target decisions, export start/menu/target naming/path/message/report decisions, share-file saved-path decisions, file/image/save/directory picker selected-path decisions, display-file external-open decisions, virtual-keyboard visibility decisions, recording lifecycle/export progress/worker decisions, cloud-upload prompt/save-before-upload decisions, cloud-browse availability and selected-download decisions, bulk cloud-upload progress decisions, tools/options app preference decisions, app status/display and renderer diagnostic decisions, app dialog metadata decisions, app frame/UI-observer decisions, app thread/task orchestration decisions, document resize decisions, layer rename/menu decisions, Tools menu/panel decisions, About menu/diagnostic decisions, main toolbar/status decisions, pano_cli classify-open, pano_cli plan-open-route, pano_cli plan-file-menu, pano_cli plan-new-document, pano_cli plan-document-file, pano_cli plan-document-version, pano_cli plan-export-start, pano_cli plan-export-menu, pano_cli plan-export-target, pano_cli plan-export-message, pano_cli plan-export-report, pano_cli plan-recording-session, pano_cli plan-app-preferences, pano_cli plan-app-status, pano_cli plan-app-dialog, pano_cli plan-app-thread, pano_cli plan-tools-menu, pano_cli plan-tools-panel, pano_cli plan-about-menu, pano_cli plan-main-toolbar, pano_cli plan-document-resize, pano_cli plan-layer-rename, pano_cli plan-layer-menu, pano_cli plan-canvas-hotkey, pano_cli plan-share-file, pano_cli plan-picked-path, pano_cli plan-display-file, pano_cli plan-keyboard-visibility, pano_cli plan-cloud-upload, pano_cli plan-cloud-browse, pano_cli plan-cloud-upload-all, and pano_cli simulate-app-session now consume pure pp_app_core route/session/export/recording/preferences/status/dialog/thread/share/platform-I/O/display/keyboard/cloud/resize/layer/tools/about/toolbar/canvas-command contracts, but document creation/loading, brush import execution, saving, export execution, tools/options UI execution, Tools panel creation/execution, About dialog/diagnostic execution, toolbar/status dialog/history/canvas execution, app dialog node creation, status/display UI rendering, renderer diagnostic capability adaptation, app task/thread execution, UI observer parent walking/callback execution, document resize execution, layer rename/menu execution, settings persistence, platform share service execution, picker service execution, display-file service execution, keyboard service execution, recording/MP4/PBO execution, cloud upload execution, and cloud browse/download execution still reach legacy Canvas::I/UI/network/video/platform singletons Avoid behavior changes while introducing component boundaries App launch and component tests; pp_app_core_document_route_tests; pp_app_core_file_menu_tests; pp_app_core_document_export_tests; pp_app_core_document_recording_tests; pp_app_core_app_frame_tests; pp_app_core_app_preferences_tests; pp_app_core_app_status_tests; pp_app_core_app_dialog_tests; pp_app_core_app_thread_tests; pp_app_core_tools_menu_tests; pp_app_core_about_menu_tests; pp_app_core_main_toolbar_tests; pp_app_core_document_resize_tests; pp_app_core_document_layer_tests; pp_app_core_document_sharing_tests; pp_app_core_document_platform_io_tests; pp_app_core_document_cloud_tests; pp_app_core_document_session_tests; pp_app_core_canvas_hotkey_tests; pano_cli classify-open --path D:/Paint/demo.ppi; pano_cli plan-open-route --path D:/Paint/demo.ppi --unsaved; pano_cli plan-file-menu --command save-as; pano_cli plan-new-document --work-dir D:/Paint --name demo --resolution-index 3 --target-exists; pano_cli plan-document-file --work-dir D:/Paint --name demo --target-exists; pano_cli plan-document-version --directory D:/Paint --doc-name demo.01 --existing-path D:/Paint/demo.02.ppi; pano_cli plan-export-start --requires-license --demo; pano_cli plan-export-menu --kind animation-mp4 --demo; pano_cli plan-export-target --kind file --work-dir D:/Paint --doc-name demo --extension .png; pano_cli plan-export-message --kind timelapse --destination success; pano_cli plan-export-report --kind license-disabled; pano_cli plan-recording-session --running --frame-count 12; pano_cli plan-recording-session --running --no-encoder; pano_cli plan-app-preferences --ui-scale 1.5 --display-density 2 --current-scale 1.6 --scale-option 1 --scale-option 1.5 --rtl; pano_cli plan-app-status --doc-name demo --unsaved --resolution 2048 --resolution-index 3 --zoom 1.25 --history-bytes 1572864 --recording-running --encoder-available --encoded-frames 12 --framebuffer-fetch --float32 --float32-linear --float16; pano_cli plan-app-dialog --kind message --cancel; pano_cli plan-app-frame; pano_cli plan-app-thread --kind ui-loop --live-reload; pano_cli plan-tools-menu --command shortcuts; pano_cli plan-tools-panel --panel layers; pano_cli plan-about-menu --command news --version-major 2 --version-minor 5 --version-fix 7; pano_cli plan-main-toolbar --command undo --undo-count 2; pano_cli plan-document-resize --current-resolution 2048 --selected-resolution-index 4; pano_cli plan-layer-rename --old-name Base --new-name Paint; pano_cli plan-layer-menu --command merge --current-index 2 --lower-name Paint; pano_cli plan-canvas-hotkey --event key-up --key z --ctrl --undo-count 2; pano_cli plan-share-file --path D:/Paint/demo.ppi; pano_cli plan-picked-path --path D:/Paint/demo.ppi; pano_cli plan-display-file --path D:/Paint/export.png; pano_cli plan-keyboard-visibility --visible; pano_cli plan-cloud-upload --new-document --unsaved; pano_cli plan-cloud-browse --selected-file demo.ppi; pano_cli plan-cloud-upload-all --file-count 3; pano_cli simulate-app-session --unsaved --save-intent save-dirty-version; pano_cli simulate-app-session --no-canvas; ctest --preset desktop-fast --build-config Debug Replace singleton reaches with context/service injection at component boundaries
DEBT-0004 Open Modernization Android, Linux, and WebGL retained CMake entrypoints now use the CMake 3.10/C++23 modernization baseline, but Android Gradle/APK, Linux app, WebGL app/package, Apple, and AppX build files remain platform-specific until root CMake alignment reaches them Prevent platform regressions during incremental migration; raw Windows .sln/.vcxproj files were removed on 2026-05-31 by user decision cmake --preset windows-msvc-default; python scripts/dev/check_retained_platform_cmake.py; platform-specific configure/build smoke checks as each platform is migrated Root CMake owns every platform source list and package path
DEBT-0005 Open Modernization Temporary local CTest harness is used before Catch2 is wired through vcpkg vcpkg is not currently on PATH, but headless tests need to run now ctest --preset desktop-fast --build-config Debug Replace tests/test_harness.h tests with Catch2 tests once vcpkg toolchain/presets are validated
DEBT-0007 Open Modernization vcpkg.json and windows-msvc-vcpkg-headless are validated for the headless Windows component matrix, and root CMake now exposes a focused panopainter_platform_build_vcpkg_ui_core target for the vcpkg-backed pp_ui_core/tinyxml2 boundary, but app targets still use vendored libraries and Android/Apple triplets are not proven Dependency migration must stay incremental while SDK/patched/vendor dependencies remain in use cmake --preset windows-msvc-vcpkg-headless; ctest --preset desktop-fast-vcpkg --build-config Debug; cmake --build --preset windows-msvc-default --config Debug --target panopainter_platform_build_vcpkg_ui_core Component targets consume vcpkg packages where reliable and desktop app, Android, and Apple triplets are validated or explicitly documented as permanent vendor exceptions
DEBT-0008 Open Modernization windows-msvc-default and windows-msvc-vcpkg-headless explicitly select Visual Studio 18 2026 for local validation, but non-VS2026 CMake executables on PATH may not know that generator The local machine has VS 2026, but using an older CMake can still default to Ninja or reject the VS 2026 generator cmake --preset windows-msvc-default; cmake --build --preset windows-msvc-default --config Debug --target PanoPainter; ctest --preset desktop-fast --build-config Debug The repo automation invokes or locates a CMake executable that supports Visual Studio 18 2026, and VS 2026 generator validation is the normal Windows path without manual tool selection
DEBT-0009 Open Modernization Android root CMake validation currently builds headless targets only, while retained standard/Quest/Focus package CMake paths now have a refreshed CMake 3.10/C++23 baseline outside root CMake; automation queries sdkmanager, installs newer or missing SDK Manager NDK/CMake packages, selects the resulting pair before configure, and reports update decisions; root CMake exposes non-default platform-build and retained native package validation targets Platform app entrypoints still live in legacy Gradle/CMake projects and need Phase 6 alignment powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64; cmake --build --preset android-x64; cmake --build --preset android-quest-arm64; cmake --build --preset android-focus-arm64; cmake --build --preset windows-msvc-default --config Debug --target panopainter_platform_build_android_assets; powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages standard; powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages quest,focus -ConfigureOnly; powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -ReadinessOnly -AndroidNativeChecks -PackageKinds android-standard-apk,android-quest-apk,android-focus-apk; cmake --build --preset windows-msvc-default --config Debug --target panopainter_android_native_package_smoke Android standard, Quest, and Focus/Wave package targets consume shared component targets and have package smoke commands
DEBT-0010 Open Modernization pp_document is a pure layer/frame/document/undo-history model with alpha-lock metadata, snapshot construction, per-layer frame metadata, renderer-free RGBA8 face payload storage, snapshot-embedded face-payload validation, renderer-free alpha8 selection-mask storage, PPI import/export helpers, stroke-script-to-face-payload CLI automation, pp_paint_renderer document face/frame compositors, renderer-neutral six-face texture upload, OpenGL command-planner validation through CLI render automation, live Canvas snapshot projection through pp_app_core/legacy_document_canvas_services, captured RGBA8 payload attachment to pp_document, live Save/Save As/Save Version/save-before-workflow snapshot-readiness reporting before retained save execution, pure app-core PPI export for payload-complete canvas snapshots, and payload-complete canvas-snapshot renderer-upload automation, but export/action-command adoption, live save-writer replacement, and renderer-owned cube-face readback ownership are not yet wired Keep extraction incremental while preserving app behavior ctest --preset desktop-fast --build-config Debug; pano_cli create-document --width 64 --height 32 --layers 2; pano_cli load-project --path tests\data\projects\minimal-project.ppi; pano_cli simulate-document-render --width 64 --height 32; pano_cli plan-canvas-document-snapshot --width 64 --height 32; pano_cli plan-canvas-document-snapshot --captured-face-payloads-per-layer 1; pp_document_tests; pp_document_ppi_import_tests; pp_document_ppi_export_tests; pp_paint_renderer_compositor_tests; pp_app_core_document_canvas_tests; pano_cli_simulate_document_edits_smoke; pano_cli_simulate_document_export_smoke; pano_cli_simulate_document_render_smoke; pano_cli_plan_canvas_document_snapshot_smoke; pano_cli_plan_canvas_document_snapshot_payload_smoke; pano_cli_save_document_project_roundtrip_smoke; pano_cli_apply_stroke_script_roundtrip_smoke; pano_cli_apply_stroke_script_rejects_tiny_canvas Legacy document behavior is represented by pp_document/pp_paint_renderer tests and the app consumes it through a boundary/facade
DEBT-0011 Open Modernization package-smoke validates the Windows CMake app artifact and launch-folder DLL payload, and reports a structured package readiness matrix for Windows AppX, Android standard/Quest/Focus APKs, Apple bundles, Linux app output, and WebGL output; the Windows app smoke passes the configure-time CMake executable so VS 2026 generator validation does not depend on cmake from PATH, retained Android package native CMake paths, and retained Linux/WebGL CMake baseline metadata are reachable from package validation and root CMake package-readiness targets, but Windows AppX/APK/Linux/Apple/WebGL package outputs are still blocked because root CMake package targets do not exist yet Platform package targets are not migrated to root CMake yet powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug; cmake --build --preset windows-msvc-default --config Debug --target panopainter_windows_app_package_smoke; powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -ReadinessOnly -AndroidNativeChecks -PackageKinds android-standard-apk,android-quest-apk,android-focus-apk; cmake --build --preset windows-msvc-default --config Debug --target panopainter_android_native_package_smoke; cmake --build --preset windows-msvc-default --config Debug --target panopainter_linux_webgl_package_readiness; python scripts/dev/check_package_smoke_readiness.py; bash -n scripts/automation/package-smoke.sh Package-smoke builds and validates Windows AppX, Android APK variants, Linux app, Apple bundles, and WebGL output where local toolchains are present
DEBT-0012 Open Modernization pp_ui_core uses vcpkg tinyxml2 on windows-msvc-vcpkg-headless, but retains pp_vendor_tinyxml2 for default and unproven platform presets Mobile/AppX/Apple triplets and app packaging still need validation before removing the vendored fallback ctest --preset desktop-fast-vcpkg --build-config Debug; ctest --preset desktop-fast --build-config Debug; powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64 All supported presets consume vcpkg tinyxml2 or document a permanent vendored exception
DEBT-0013 Open Modernization pp_assets, pp_document, pano_cli inspect-project, pano_cli load-project, and pano_cli save-project validate the fixed PPI header, thumbnail/body byte layout, generated multi-layer/multi-frame PPI writing with explicit layer opacity/blend/alpha-lock/visibility metadata, per-layer frame durations, metadata-only and targeted dirty-face-payload save/load round-trips, layer/frame index, dirty-face descriptors, dirty-face PNG payload metadata, asset-level RGBA PNG payload decoding, pure document-to-PPI export, CLI document export automation, file-writing document export automation, stroke-script-generated document payload export, decoded pixel attachment to pp_document, live save-path snapshot-readiness reporting, and app-core canvas-snapshot-to-PPI export automation, but full legacy PPI round-trip parity and pure live save writer replacement are not yet extracted Full PPI save parity requires staged extraction of legacy Canvas serialization and image/layer payload handling ctest --preset desktop-fast --build-config Debug; pp_assets_image_pixels_tests; pp_assets_ppi_header_tests; pp_document_ppi_import_tests; pp_document_ppi_export_tests; pano_cli_inspect_project_layout_smoke; pano_cli_load_project_metadata_smoke; pano_cli_save_project_roundtrip_smoke; pano_cli_save_project_payload_roundtrip_smoke; pano_cli_simulate_document_export_smoke; pano_cli_save_document_project_roundtrip_smoke; pano_cli_apply_stroke_script_roundtrip_smoke; pano_cli_apply_stroke_script_rejects_tiny_canvas Full PPI load/save fixtures cover thumbnails, decoded layer face payloads attached to documents, frames, corrupt payloads, dirty-face payload saving, arbitrary legacy canvas payload/layout combinations, and legacy app round-trip compatibility
DEBT-0014 Open Modernization windows-clangcl-asan now configures as a headless Ninja/clang-cl preset and uses the release MSVC runtime required by ASan, but local builds still fail because installed clang-cl 18.1.8 is paired with VS 2026-preview STL headers that require Clang 20 or newer Sanitizer validation should be local and repeatable, but this machine's compiler/header pairing is incompatible cmake --fresh --preset windows-clangcl-asan; cmake --build --preset windows-clangcl-asan --target pp_foundation Install/use Clang 20+ with the VS 2026 STL, or point the preset at a compatible VS 2022 toolchain, then make platform-build.ps1 -Presets windows-clangcl-asan pass for the headless matrix
DEBT-0015 Open Modernization Cursor visibility requests now consume pure pp_app_core planning through pano_cli plan-cursor-visibility, App::show_cursor/App::hide_cursor dispatch through PlatformServices without platform guards, and Windows live execution uses injected WindowsPlatformServices, but macOS cursor execution still reaches the retained fallback adapter Keep canvas cursor behavior stable while platform shells are extracted incrementally pp_app_core_document_platform_io_tests; pano_cli plan-cursor-visibility --visible; ctest --preset desktop-fast --build-config Debug Cursor visibility execution is owned by injected pp_platform_* services for every supported platform
DEBT-0016 Open Modernization Clipboard get/set requests now consume pure pp_app_core planning through pano_cli plan-clipboard-read and pano_cli plan-clipboard-write, and Windows live execution uses injected WindowsPlatformServices, but Apple/Android clipboard execution still reaches retained fallback adapter branches from App::clipboard_get_text and App::clipboard_set_text Keep picker/color text clipboard behavior stable while platform shells are extracted incrementally pp_app_core_document_platform_io_tests; pano_cli plan-clipboard-write --text #ff00aa; ctest --preset desktop-fast --build-config Debug Clipboard execution is owned by injected pp_platform_* services for every supported platform
DEBT-0017 Open Modernization Startup storage path preparation, App::clipboard_get_text, App::clipboard_set_text, App::show_cursor, App::hide_cursor, App::showKeyboard, App::hideKeyboard, App::display_file, App::share_file, native app/window close, UI-thread lifecycle hooks, render-context acquire/release/present hooks, render-target binding hooks, render platform hint hooks, render debug callback hooks, render-capture frame hooks, recording cleanup, live asset/layout reload policy, diagnostic stacktrace/crash hooks, per-frame platform hooks, App::pick_image, App::pick_file, the non-writer App::pick_file_save, App::pick_dir, working-directory picker/display-path policy, canvas input tip/pressure policy, prepared-file save/download handoff, work-directory document export collection policy, app network TLS verification policy, PPBR export data-directory policy, SonarPen availability/startup, and VR mode start/stop now call the SDK-free pp::platform::PlatformServices interface, and Windows injects WindowsPlatformServices from src/platform_windows/windows_platform_services.*; Windows render-platform hint and debug-output state token/enable sequencing now delegates to tested pp_renderer_gl helpers, leaving Windows with context, callback, console, and Win32 ownership; the retained macOS fallback render-platform hint enable sequence also delegates to the same tested pp_renderer_gl helper; non-Windows live implementations still use src/platform_legacy/legacy_platform_services.*, a named fallback adapter that forwards to retained Apple/Android/Linux/Web bridge functions and retained no-op branches, including retained iOS canvas tip behavior, retained macOS directory picker/display-path behavior, retained iOS SonarPen bridge, retained non-Windows VR unsupported/no-op behavior, and retained macOS PPBR export directory override; pp_platform_api also owns the default network TLS policy helper consumed by retained curl sites that cannot yet depend on injected services Preserve behavior while moving platform execution behind a testable service boundary before platform shell implementations are injected pp_platform_api_tests; pp_app_core_document_export_tests; pp_app_core_document_platform_io_tests; ctest --preset desktop-fast --build-config Debug; powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug Replace src/platform_legacy/legacy_platform_services.* with injected pp_platform_* service implementations owned by each non-Windows platform shell
DEBT-0019 Open Modernization Unreferenced-parameter warnings are muted globally through pp_project_warnings with MSVC /wd4100 and Clang/GCC -Wno-unused-parameter Legacy callbacks, virtual hooks, serializer methods, and platform/API compatibility functions carry many intentionally unused parameters during the component split; muting this keeps stricter warning builds focused on higher-signal migration issues cmake --build --preset windows-msvc-default --config Debug --target PanoPainter; ctest --preset desktop-fast --build-config Debug; cmake --build --preset linux-clang --target pp_foundation Remove /wd4100 and -Wno-unused-parameter, mark intentionally unused parameters with names/comments or [[maybe_unused]], and make the Windows app plus headless Clang/GCC tests pass without unreferenced-parameter warnings
DEBT-0020 Open Modernization Document resize dialog state, selected-resolution planning, and execution dispatch now consume pure pp_app_core through NodeDialogResize, App::dialog_resize, pano_cli plan-document-resize, and the DocumentResizeServices boundary, and live resize shares src/legacy_document_canvas_services.* with canvas clear commands, but the shared live bridge still calls legacy Canvas::resize, updates the legacy app title, and clears legacy ActionManager history through the history bridge Preserve existing layer/frame GPU resize behavior while the document model and canvas execution boundary are extracted incrementally pp_app_core_document_resize_tests; pano_cli plan-document-resize --current-resolution 2048 --selected-resolution-index 4; ctest --preset desktop-fast --build-config Debug Document resize execution is owned by injected document/app services with no legacy resize adapter, title shim, or direct ActionManager history clearing
DEBT-0021 Open Modernization Layer rename planning/execution dispatch, layer panel operation planning/execution dispatch, and layer panel selected-control/visibility view projection now consume pure pp_app_core through App::dialog_layer_rename, App::init_sidebar layer callbacks, NodePanelLayer::update_attributes(), pano_cli plan-layer-rename, pano_cli plan-layer-operation, pano_cli plan-layer-panel-view, DocumentLayerRenameServices, and DocumentLayerOperationServices, and the live execution adapters are centralized in src/legacy_document_layer_services.*, but that shared bridge and panel adapter still mutate legacy Canvas layer state, NodeLayer/NodePanelLayer, and ActionManager undo entries Preserve existing UI/canvas behavior while document layer commands, panel projection, and undo history are extracted incrementally pp_app_core_document_layer_tests; pano_cli plan-layer-rename --old-name Base --new-name Paint; pano_cli plan-layer-operation --kind add --layer-count 2 --index 1 --name Paint; pano_cli plan-layer-panel-view --layer-count 3 --current-index 1 --hidden-index 2 --locked-index 1 --current-opacity 0.25 --current-blend-mode 4; ctest --preset desktop-fast --build-config Debug Layer command execution and panel state projection are owned by the document/app command boundary with legacy Canvas/UI nodes acting only as adapters or removed entirely
DEBT-0022 Open Modernization Animation panel frame command planning, panel action planning, panel view-model projection, timeline scrub planning, panel-control/timeline execution dispatch, selected-frame click dispatch, playback tick stepping, and play-mode toggles now consume pure pp_app_core through NodePanelAnimation, NodeAnimationTimeline, pano_cli plan-animation-operation, pano_cli plan-animation-panel-action, pano_cli plan-animation-panel-view, pano_cli plan-animation-timeline-scrub, and DocumentAnimationServices; live execution is centralized in src/legacy_document_animation_services.*, but that bridge still mutates or reads legacy Canvas/Layer frame state, canvas mode, animation-panel timeline/playback fields, and uses a temporary NodePanelAnimation friend adapter Preserve existing animation panel behavior while timeline/frame commands move toward the document/app command boundary pp_app_core_document_animation_tests; pano_cli plan-animation-operation --kind add --frame-count 2 --current-frame 0; pano_cli plan-animation-operation --kind select --frame-count 3 --selected-frame 1 --layer-index 2 --layer-id 42; pano_cli plan-animation-operation --kind playback --total-duration 5 --current-frame 4 --offset 1; pano_cli plan-animation-operation --kind toggle-playback --playing; pano_cli plan-animation-panel-action --action next --total-duration 5 --current-frame 4; pano_cli plan-animation-panel-view --layer-count 2 --frame-count 3 --total-duration 6 --current-layer 1 --current-frame 4; pano_cli plan-animation-timeline-scrub --total-duration 5 --cursor-x 174.99; ctest --preset desktop-fast --build-config Debug Animation frame/timeline/playback execution is owned by injected document/app timeline services with no legacy Canvas/Layer/canvas-mode adapter and UI nodes acting only as adapters or removed entirely
DEBT-0023 Open Modernization Brush/color/preset/stroke-settings UI planning, texture-list add/remove/reorder planning, brush preset-list add/select/move/remove/clear planning, stroke-panel slider/toggle/blend/reset planning, stroke-panel view projection, app-level brush refresh view projection, and execution dispatch now consume pure pp_app_core through App::init_sidebar, App::brush_update(), NodePanelBrush, NodePanelBrushPreset, NodePanelStroke, NodePanelStroke::update_controls(), restored/docked floating-panel callbacks, pano_cli plan-brush-operation, pano_cli plan-brush-refresh, pano_cli plan-brush-texture-list, pano_cli plan-brush-preset-list, pano_cli plan-brush-stroke-control, pano_cli plan-brush-stroke-panel-view, BrushUiServices, BrushTextureListServices, BrushPresetListServices, and BrushStrokeControlServices, and live execution is centralized in src/legacy_brush_ui_services.* or narrow legacy service bridges where possible, but preset-list execution still mutates legacy NodePanelBrushPreset child nodes directly while the bridge and panel adapter still mutate/read legacy Brush/Canvas::I, load/save legacy brush texture images, apply retained legacy quick/stroke/color widget writes, own brush thumbnail paths and popup behavior, and use temporary NodePanelBrush/NodePanelBrushPreset friend adapters to reach private list state Preserve existing brush UI behavior while brush commands and view projection move toward a brush/app/asset command boundary and asset-managed texture/preset selection pp_app_core_brush_ui_tests; pano_cli plan-brush-operation --kind color --r 0.25 --g 0.5 --b 0.75 --a 1; pano_cli plan-brush-operation --kind pattern --path data/patterns/noise.png --thumb data/patterns/thumbs/noise.png; pano_cli plan-brush-refresh --floating-picker --tip-flow 0.8 --tip-size 48 --r 0.2 --g 0.3 --b 0.4 --a 1; pano_cli plan-brush-texture-list --kind add --dir brushes --data-path data --source C:/Temp/soft.png; pano_cli plan-brush-preset-list --kind remove --item-count 1 --current-index 0; pano_cli plan-brush-stroke-control --kind float --setting tip-size --value 42.5; pano_cli plan-brush-stroke-control --kind blend --setting pattern --blend-mode 3; pano_cli plan-brush-stroke-panel-view --tip-size 64 --jitter-scatter 0.4 --dual-disabled --tip-blend-mode 2 --pattern-blend-mode 5; ctest --preset desktop-fast --build-config Debug Brush color/texture/preset/stroke-settings, texture-list, preset-list, stroke-control execution, stroke-panel projection, and brush refresh projection are owned by injected brush/app/asset/UI services with no legacy brush/canvas adapter, direct NodePanelBrushPreset child mutation, brush thumbnail/popup ownership, legacy quick/stroke/color widget writes, or brush-panel friend access
DEBT-0024 Open Modernization Grid/heightmap/lightmap UI planning and execution dispatch now consume pure pp_app_core through NodePanelGrid, pano_cli plan-grid-operation, and the GridUiServices boundary; live execution is centralized in src/legacy_grid_ui_services.*, and retained CPU lightmap row dispatch now uses shared parallel_for instead of platform-specific Win32/Apple worker APIs, but the bridge still performs legacy image loading, OpenGL texture updates, nanort lightmap baking/progress, and Canvas::draw_objects commit execution Preserve grid/lightmap behavior while moving renderable grid commands toward app/renderer/document boundaries pp_app_core_grid_ui_tests; pano_cli plan-grid-operation --kind render --float32 --texture-resolution 1024 --samples 32; ctest --preset desktop-fast --build-config Debug Grid heightmap/lightmap execution is owned by app/renderer/document services with NodePanelGrid acting only as UI adapter
DEBT-0025 Open Modernization Quick brush/color slot, mini-state, and size/flow slider preview planning now consume pure pp_app_core through NodePanelQuick, pano_cli plan-quick-operation, pano_cli plan-quick-slider-preview, and the QuickUiServices boundary; live slot/popup/restore/reset execution is centralized in src/legacy_quick_ui_services.*, and live slider callbacks now consume pp_app_core preview cursor/tip planning directly, but the bridge and panel adapter still mutate legacy quick UI widgets, Brush previews, color picker popup state, preset popup state, and direct legacy CanvasModePen/CanvasModeLine fields Preserve quick-panel behavior while quick brush/color and slider preview commands move toward a brush/app command boundary with safer automation coverage pp_app_core_quick_ui_tests; pano_cli plan-quick-operation --kind brush --current-index 0 --slot-index 2; pano_cli plan-quick-operation --kind restore --brush-index 2 --color-index 1 --fire-event; pano_cli plan-quick-slider-preview --slider-x 10 --slider-y 20 --slider-height 40 --zoom 2 --pen-mode --no-line-mode; ctest --preset desktop-fast --build-config Debug Quick-panel selection, popup, restore, reset, brush preview, color execution, and slider preview mode updates are owned by injected app/brush/UI/canvas services with no legacy quick-panel adapter, popup adapter, direct brush-preview mutation, or direct CanvasMode* field writes
DEBT-0026 Open Modernization Toolbar history command planning and canvas hotkey history dispatch now consume pure pp_app_core through App::init_toolbar_main, NodeCanvas, pano_cli plan-history-operation, and the HistoryUiServices boundary, and both live callers share src/legacy_history_services.* for saturated legacy history metrics and execution, but the shared live bridge still mutates legacy ActionManager stacks directly Preserve undo/redo/clear behavior while moving action history toward document/app command services pp_app_core_history_ui_tests; pano_cli plan-history-operation --kind undo --undo-count 2; pano_cli plan-history-operation --kind clear --undo-count 2 --redo-count 1 --memory-bytes 4096; ctest --preset desktop-fast --build-config Debug Undo/redo/clear execution is owned by injected document/app history services with no legacy ActionManager adapter
DEBT-0027 Open Modernization Canvas draw-tool toolbar command, full draw-toolbar binding planning, canvas input mode switching, active-state planning/execution dispatch, canvas cursor visibility planning, and canvas keyboard/touch command planning now consume pure pp_app_core through App::init_toolbar_draw, App::update, NodeCanvas, pano_cli plan-canvas-tool, pano_cli plan-canvas-tool-toolbar, pano_cli plan-canvas-tool-state, pano_cli plan-canvas-cursor, pano_cli plan-canvas-hotkey, CanvasToolServices, and CanvasHotkeyServices, live toolbar/input/hotkey execution is centralized in src/legacy_canvas_tool_services.*, NodeCanvas::update_cursor() consumes the cursor planner before retained platform cursor dispatch, and canvas mode tip visibility plus pressure remapping now route through PlatformServices, but the bridge still mutates or reads legacy Canvas mode state, CanvasModePen drawing/resizing/picking state, touch-lock state, transform copy/cut action objects, ActionManager, legacy save UI, legacy stroke size controls, retained NodeButton/NodeButtonCustom lookup, and cursor/UI singletons Preserve current toolbar, stylus eraser, cursor visibility, keyboard, and touch command behavior while canvas input/tools move toward an app/document command boundary pp_app_core_canvas_tool_ui_tests; pp_app_core_canvas_hotkey_tests; pp_platform_api_tests; pano_cli plan-canvas-tool --kind copy; pano_cli plan-canvas-tool-toolbar; pano_cli plan-canvas-tool-state --mode draw --picking --touch-lock; pano_cli plan-canvas-cursor --mode draw --visibility small-brush --brush-size 9.5; pano_cli plan-canvas-cursor --visibility small-brush --bad-size; pano_cli plan-canvas-hotkey --event key-up --key z --ctrl --undo-count 2; pano_cli plan-canvas-hotkey --event key-up --key s --ctrl --shift; ctest --preset desktop-fast --build-config Debug Canvas tool selection, toolbar binding, toolbar state refresh, picking, touch lock, stylus eraser/key mode switching, cursor visibility planning/execution, hotkey/touch command dispatch, save hotkeys, history hotkeys, brush-size hotkeys, and transform action execution are owned by injected app/document/canvas services with no legacy toolbar/canvas adapter
DEBT-0028 Open Modernization Canvas clear command planning and execution dispatch now consume pure pp_app_core through App::init_toolbar_main, Layer menu clear, pano_cli plan-canvas-clear, and the DocumentCanvasClearServices boundary, and toolbar/Layer-menu clear share src/legacy_document_canvas_services.*, but the shared live bridge still calls legacy Canvas::clear, which records ActionLayerClear, clears the current layer/frame, and marks legacy Canvas::I unsaved Preserve clear-current-layer behavior while canvas/document commands move toward document/app command services pp_app_core_document_canvas_tests; pano_cli plan-canvas-clear --r 0 --g 0.1 --b 0.2 --a 0.3; pano_cli plan-canvas-clear --no-canvas; pano_cli plan-layer-menu --command clear --current-index 1 --current-name Paint; ctest --preset desktop-fast --build-config Debug Canvas clear execution, undo recording, dirty-state updates, and clear color handling are owned by injected document/app services with no legacy canvas-clear adapter
DEBT-0029 Open Modernization Image import route planning and execution dispatch now consume pure pp_app_core through the File menu, pano_cli plan-image-import, and the DocumentImageImportServices boundary, and live File-menu import execution is centralized in src/legacy_app_shell_services.*, but the bridge still loads images with legacy Image, calls legacy Canvas::import_equirectangular, or configures legacy import transform mode directly Preserve current File > Import behavior while image import moves toward document/app/asset command services pp_app_core_document_import_tests; pano_cli plan-image-import --width 4096 --height 2048; pano_cli plan-image-import --width 1024 --height 1024; ctest --preset desktop-fast --build-config Debug Image loading, equirectangular import, transform-placement import, and failure reporting are owned by injected document/app/asset services with File-menu callbacks acting only as adapters and no legacy image-import adapter
DEBT-0030 Open Modernization File export menu action planning and execution dispatch now consume pure pp_app_core through the File menu, pano_cli plan-export-menu, and the DocumentExportMenuServices boundary, and live execution is centralized in src/legacy_app_shell_services.*, but the bridge still opens legacy export dialogs and then reaches legacy canvas/render/video export code Preserve current export menu behavior while export command execution moves toward document/app/renderer/video services pp_app_core_document_export_tests; pano_cli plan-export-menu --kind png; pano_cli plan-export-menu --kind animation-mp4 --demo; pano_cli plan-export-menu --kind layers --no-canvas; ctest --preset desktop-fast --build-config Debug Export menu routing, license gating, target creation, image/layer/cube/depth/animation/timelapse execution, and error reporting are owned by injected document/app/renderer/video services with File-menu callbacks acting only as UI adapters and no legacy export adapter
DEBT-0031 Open Modernization Top-level File menu command planning and execution dispatch now consume pure pp_app_core through App::init_menu_file, pano_cli plan-file-menu, and the FileMenuServices boundary, and live execution is centralized in src/legacy_app_shell_services.*, but the bridge still invokes legacy dialogs, platform pickers, cloud code, share code, and canvas import/export paths directly Preserve File menu behavior while app workflows move toward app/document/platform command services pp_app_core_file_menu_tests; pano_cli plan-file-menu --command save-as; pano_cli plan-file-menu --command import; pano_cli plan-file-menu --command cloud-upload; ctest --preset desktop-fast --build-config Debug File menu routing, picker dispatch, save/share/cloud/resize/export execution, and image/project import execution are owned by injected app/document/platform services with App::init_menu_file acting only as a UI adapter and no legacy File menu adapter
DEBT-0032 Open Modernization Layer menu command planning and execution dispatch now consume pure pp_app_core through App::init_menu_layer, pano_cli plan-layer-menu, and the DocumentLayerMenuServices boundary; Layer menu clear reuses the DocumentCanvasClearServices executor; and Layer menu rename/clear/merge now share src/legacy_document_layer_services.*, but the bridge still calls the legacy rename dialog path, NodePanelLayer::merge, and reads Canvas::I animation/layer state directly Preserve existing Layer menu behavior while layer commands move toward document/app services pp_app_core_document_layer_tests; pano_cli plan-layer-menu --command clear --current-index 1 --current-name Paint; pano_cli plan-layer-menu --command merge --current-index 2 --lower-name Paint; pano_cli plan-layer-merge --layer-count 3 --from-index 2 --to-index 1; pano_cli plan-layer-merge --layer-count 3 --from-index 2 --to-index 1 --animation-duration 3; pano_cli plan-layer-menu --command rename --no-current-layer; ctest --preset desktop-fast --build-config Debug Layer rename, merge-down execution, animation gating, and selected-layer state are owned by injected document/app services with Layer-menu callbacks acting only as UI adapters and no legacy Layer menu adapter
DEBT-0033 Open Modernization Tools menu planning and direct command execution dispatch now consume pure pp_app_core through App::init_menu_tools, pano_cli plan-tools-menu, pano_cli plan-tools-panel, pano_cli plan-canvas-camera-reset, pano_cli plan-canvas-view-density, pano_cli plan-canvas-view-cursor-mode, and the ToolsMenuServices boundary, direct command execution is centralized in src/legacy_app_shell_services.*, SonarPen availability/startup now routes through PlatformServices, and reset-camera, viewport-density, and cursor-mode execution now share src/legacy_canvas_view_services.*, but live adapters still construct legacy NodePanelFloating panels, mutate legacy panel nodes, clear CanvasModeGrid, open legacy shortcuts UI, mutate retained Canvas camera/density/cursor state, write retained Settings, and rely on the legacy platform adapter for the retained iOS SonarPen bridge Preserve current Tools menu and canvas-view behavior while UI shell actions move toward app/UI/platform/canvas services pp_app_core_tools_menu_tests; pp_app_core_canvas_view_tests; pp_platform_api_tests; pano_cli plan-tools-menu --command shortcuts; pano_cli plan-tools-panel --panel layers; pano_cli plan-tools-panel --panel animation --already-visible; pano_cli plan-canvas-camera-reset; pano_cli plan-canvas-view-density --density 1.5; pano_cli plan-canvas-view-cursor-mode --mode 3; ctest --preset desktop-fast --build-config Debug Tools panel creation, submenu routing, grid clear, camera reset, viewport density, cursor mode, shortcuts dialog, and SonarPen dispatch are owned by injected app/UI/platform/canvas services with App::init_menu_tools and options callbacks acting only as UI adapters and no legacy Tools/canvas-view adapter
DEBT-0034 Open Modernization About menu command planning and execution dispatch now consume pure pp_app_core through App::init_menu_about, pano_cli plan-about-menu, and the AboutMenuServices boundary, and live execution is centralized in src/legacy_app_shell_services.*, but the bridge still opens legacy About/manual/what's-new dialogs, invokes the injected crash hook, and runs the legacy Canvas stroke performance test directly Preserve About menu behavior while dialogs and diagnostics move toward app/UI/platform services pp_app_core_about_menu_tests; pano_cli plan-about-menu --command news --version-major 2 --version-minor 5 --version-fix 7; pano_cli plan-about-menu --command performance --no-canvas; ctest --preset desktop-fast --build-config Debug About/manual/what's-new dialog dispatch, crash-test dispatch, and performance-test execution are owned by injected app/UI/platform services with App::init_menu_about acting only as a UI adapter and no legacy About adapter
DEBT-0035 Open Modernization Main toolbar/status command planning and execution dispatch now consume pure pp_app_core through App::init_toolbar_main, pano_cli plan-main-toolbar, and the MainToolbarServices boundary; toolbar test-message dialog metadata now lives in pp_app_core through plan_main_toolbar_message_dialog, retained message-box creation routes through src/legacy_app_dialog_services.*, history/canvas commands now hand off through HistoryUiServices and DocumentCanvasClearServices, and live execution is centralized in src/legacy_app_shell_services.*, but the bridge still opens legacy open/save/settings dialogs, stores raw compatibility pointers, and delegates to legacy history/canvas adapters Preserve reachable toolbar/status behavior while app shell commands move toward app/document/UI services pp_app_core_main_toolbar_tests; pano_cli plan-main-toolbar --command undo --undo-count 2; pano_cli plan-main-toolbar --command message-box; pano_cli plan-main-toolbar --command clear-canvas --no-canvas; ctest --preset desktop-fast --build-config Debug Open/save/settings/message-box routing, undo/redo/clear-history execution, and canvas-clear execution are owned by injected app/document/UI services with App::init_toolbar_main acting only as a UI adapter and no legacy toolbar adapter
DEBT-0036 Open Modernization pp_renderer_api, pp_paint_renderer, pano_cli plan-paint-feedback, and pano_cli plan-stroke-composite can choose backend-neutral complex paint feedback strategies for fixed-function blending, framebuffer-fetch-capable renderers, or ping-pong render targets. OpenGL extension detection now stores pp::renderer::RenderDeviceFeatures through ShaderManager, using pp_renderer_gl::query_opengl_capability_detection, detect_opengl_feature_state, and render_device_features as the backend conversion point; that feature snapshot now includes float32-linear filtering, so canvas stroke texture format selection, renderer diagnostics, grid lightmap render planning, and grid bake target selection no longer read ShaderManager::ext_* flags directly. pp_paint_renderer::plan_canvas_blend_gate owns the compatibility mapping from persisted layer/brush blend indices to the extracted stroke-composite planner, and live Canvas::draw_merge plus NodeCanvas panorama rendering both call it with the stored renderer-neutral feature set for their existing shader-blend gates and destination-copy versus framebuffer-fetch decisions. pp_paint_renderer::plan_canvas_stroke_feedback also owns the current destination-feedback decision, and live Canvas::stroke_draw, thumbnail layer blending, and NodeStrokePreview brush-preview rendering use it for framebuffer-fetch versus destination-copy decisions. Pure document-frame compositing and renderer-neutral six-face texture upload are now covered both by simulate-document-render and payload-complete plan-canvas-document-snapshot automation. The retained copy_framebuffer_to_texture_2d utility bridge now routes 2D framebuffer-to-texture copies through tested pp_renderer_gl dispatch, retained RTT::create/RTT::destroy render-target texture parameter setup, optional depth renderbuffer allocation, framebuffer allocation/attachment/status checks, binding restore, and resource deletion now route through tested pp_renderer_gl dispatch, retained RTT clear, masked clear with color-write-mask restore, texture bind/unbind, and RGBA8 dirty-region texture writes now route through tested pp_renderer_gl dispatch, retained Canvas, NodeCanvas, and NodeStrokePreview texture-unit switches now route through tested active-texture dispatch, retained Canvas, NodeCanvas, NodeStrokePreview, and desktop HMD viewport/scissor/capability execution now route through tested pp_renderer_gl dispatch adapters, retained NodeCanvas, CanvasMode, and NodePanelGrid capability-state snapshots now route through tested pp_renderer_gl query dispatch, CanvasLayer cube/equirect generation plus frame clears now share legacy_ui_gl_dispatch for active-texture selection, cube texture binding, viewport execution, blend capability execution, clear-color query/restore, and color-buffer clear adapter endpoints backed by tested pp_renderer_gl dispatch helpers, while the cube-face framebuffer-to-texture copy now uses the shared retained copy_framebuffer_to_texture_target utility bridge backed by tested pp_renderer_gl dispatch and remains tracked by DEBT-0036 until renderer services own copy execution, NodePanelGrid live heightmap draw and bake setup now route depth/blend state, depth clears, color-write-mask toggles, active texture selection, bake viewport execution, sun-overlay viewport query, and desktop texture-resize readback through tested pp_renderer_gl dispatch adapters; its active-texture, depth/blend capability query/apply, viewport query/execution, depth-clear, and color-write-mask adapter endpoints now share legacy_ui_gl_dispatch, retained CanvasMode overlay/mask/transform paths now route active texture, depth/blend state, transform/cut viewport execution, paint-mode blend/depth state snapshots, and canvas-tip pick framebuffer readback through tested pp_renderer_gl dispatch adapters; their active-texture, capability query/apply, viewport, read-framebuffer query, and RGBA8 pixel-readback adapter endpoints now share legacy_ui_gl_dispatch, retained simple UI draw paths now share legacy_ui_gl_dispatch for blend-state execution, fallback 2D texture unbinds, NodeViewport viewport query/restore, color-buffer clears, and clear-color restore, retained NodeCanvas plus NodeStrokePreview draw-state paths now share legacy_ui_gl_dispatch for active-texture selection, fallback texture unbind, viewport/scissor execution, viewport and clear-color queries, color-buffer clears, clear-color restore, and capability query/apply adapter endpoints backed by tested pp_renderer_gl dispatch helpers, retained Canvas stroke/object/thumbnail/export/frame-clear state endpoints now share legacy_ui_gl_dispatch for active-texture selection, fallback texture unbind, viewport/scissor execution, viewport and clear-color query, clear-color restore, and capability query/apply adapter endpoints, retained Canvas and RTT depth renderbuffer allocation/attachment/delete now share legacy_gl_renderbuffer_dispatch while renderer-resource ownership remains retained, retained Texture2D, TextureCube, and RTT texture allocation/delete/bind/parameter/update/mipmap dispatch now share legacy_gl_texture_dispatch while renderer-resource ownership remains retained, retained Texture2D readback plus RTT framebuffer allocation/delete/bind/restore/blit/readback and PBO readback dispatch now share legacy_gl_framebuffer_dispatch while renderer-resource ownership remains retained, retained Sampler create/parameter/border/bind dispatch shares legacy_gl_sampler_dispatch and retained PBO allocation/readback/map/unmap/delete dispatch shares legacy_gl_pixel_buffer_dispatch while renderer-resource ownership remains retained, retained Shape, TextMesh, and NodeColorWheel mesh buffer/VAO creation/upload/draw/delete dispatch now share legacy_gl_mesh_dispatch while renderer-resource ownership remains retained, retained shader source compilation/deletion, program attach/link/use/delete, attribute rebinding/location lookup, active-uniform enumeration, uniform-location discovery, and uniform writes now share legacy_gl_shader_dispatch while shader-program ownership remains retained, retained gl_state save/restore and copy_framebuffer_to_texture_target now reuse shared retained UI/framebuffer/shader/sampler bridge callbacks while renderer state and framebuffer-copy execution remain retained, retained app startup, app clear, app UI viewport/scissor, command-convert renderer state, and desktop VR draw-state endpoints now share legacy_ui_gl_dispatch while app/VR renderer execution remains retained, retained RTT clear and masked-clear endpoints now share legacy_ui_gl_dispatch while RTT render-target execution remains retained, retained app startup logging, Windows early context logging/window-title detection, and shader capability detection now share legacy_gl_runtime_dispatch while runtime/capability probing remains retained, and retained CanvasLayer frame-clear draw-state paths route saved clear-color query and restore through the same tested helpers, but actual live stroke rasterization, dual-brush compositing, pattern feedback math, thumbnail layer compositing, brush-preview compositing, grid lightmap/heightmap mesh rendering, and the retained ShaderManager::ext_* compatibility fields still use legacy OpenGL canvas/UI execution Preserve current painting behavior while the renderer boundary matures for OpenGL parity and later Vulkan/Metal experiments pp_renderer_api_tests; pp_renderer_gl_capabilities_tests; pp_paint_renderer_compositor_tests; pano_cli plan-paint-feedback --framebuffer-fetch --explicit-transitions --render-only; pano_cli plan-paint-feedback --texture-copy; pano_cli plan-stroke-composite --stroke-blend 10 --framebuffer-fetch --explicit-transitions --render-only; pano_cli plan-stroke-composite --layer-blend 4 --dual-blend --texture-copy; pano_cli plan-canvas-document-snapshot --captured-face-payloads-per-layer 1; ctest --preset desktop-fast --build-config Debug; cmake --build --preset windows-msvc-default --config Debug --target PanoPainter Live stroke/layer compositing chooses its feedback path through pp_paint_renderer and renderer services, with OpenGL golden parity and Vulkan/Metal lab tests covering framebuffer-fetch and ping-pong behavior
DEBT-0037 Open Modernization Recording lifecycle/export planning, export progress-dialog metadata, and execution dispatch now consume pure pp_app_core through App::rec_start, App::rec_stop, App::rec_clear, App::rec_export, pano_cli plan-recording-session, and the RecordingServices boundary; live execution is centralized in src/legacy_recording_services.*, retained MP4 export progress bar creation routes through src/legacy_app_dialog_services.*, and retained PBO allocation/readback/map/unmap/delete operations now route through tested pp_renderer_gl dispatch, but the bridge still owns legacy recording thread startup/shutdown, platform recorded-file cleanup, progress lifetime, retained App::rec_loop readback call sites, and MP4Encoder::write_mp4 execution Preserve current timelapse/MP4 behavior while recording moves toward app/document/renderer/video services pp_app_core_document_recording_tests; pp_renderer_gl_capabilities_tests; pano_cli plan-recording-session --running --frame-count 12; pano_cli plan-recording-session --platform-deletes-recorded-files; ctest --preset desktop-fast --build-config Debug Recording thread lifecycle, frame readback scheduling, platform cleanup, progress reporting, and MP4 writing are owned by injected app/renderer/video services with App methods acting only as adapters
DEBT-0038 Open Modernization Cloud upload/browse/bulk planning, cloud prompt/progress metadata, and execution dispatch now consume pure pp_app_core through App::cloud_upload, App::cloud_upload_all, App::cloud_browse, pano_cli plan-cloud-upload, pano_cli plan-cloud-upload-all, pano_cli plan-cloud-browse, pano_cli plan-cloud-transfer, and the CloudServices boundary; live execution is centralized in src/legacy_cloud_services.*, the app-owned upload/download CURL helpers now consume pp_app_core cloud transfer request/progress planning and the platform TLS-verification bypass policy before retained CURL setup, upload warning/publish/success prompts, upload/bulk progress dialog titles, download-progress prompt metadata, and formatted download progress text come from tested app-core plans, retained cloud prompt/progress creation routes through src/legacy_app_dialog_services.* via App::message_box/App::show_progress or direct bridge calls, and retained Asset::open_url, LogRemote::net_init, and NodeDialogCloud::load_thumbs_thread curl sites consume the pp_platform_api default TLS policy helper instead of spelling Android branches locally, but the bridge still uses legacy save-before-upload, app-owned curl helpers instead of an injected network service, upload form construction, response/error handling, retained prompt/progress lifetime, OpenGL context guarding, NodeDialogCloud, Canvas project open, layer refresh, and ActionManager reset Preserve current cloud behavior while cloud/network/document import flows move toward app/document/platform services pp_app_core_document_cloud_tests; pp_platform_api_tests; pano_cli plan-cloud-upload --new-document --unsaved; pano_cli plan-cloud-browse --selected-file demo.ppi; pano_cli plan-cloud-upload-all --file-count 3; pano_cli plan-cloud-transfer --direction download --progress --disable-tls-verification; ctest --preset desktop-fast --build-config Debug Cloud upload/download, TLS policy, save-before-upload, progress reporting, cloud browse dialog, downloaded project opening, layer refresh, OpenGL context ownership, and action-history reset are owned by injected app/document/network/platform/renderer services with App methods acting only as adapters
DEBT-0039 Open Modernization Document-open planning and execution dispatch now consume pure pp_app_core through App::open_document, pano_cli plan-open-route, DocumentOpenServices, and src/legacy_document_open_services.*, but the bridge still opens ABR/PPBR import prompts before delegating import execution to src/legacy_brush_package_import_services.*, applies unsaved-project discard prompts, calls legacy project-open execution, refreshes layer UI, updates the app title, and clears legacy history directly Preserve current file-open/import behavior while document loading and brush import move toward app/document/asset/UI services pp_app_core_document_route_tests; pp_app_core_document_session_tests; pano_cli plan-open-route --path D:/Paint/Scenes/demo.ppi --unsaved; pano_cli plan-open-route --path D:/Paint/Brushes/clouds.ABR --unsaved; ctest --preset desktop-fast --build-config Debug Brush import prompting, project-open execution, unsaved-project discard prompting, layer refresh, title updates, and history clearing are owned by injected app/document/asset/UI services with App::open_document acting only as an adapter
DEBT-0040 Open Modernization Close request, document save, save-before-workflow planning/execution dispatch, and close/save-before/save-error prompt metadata now consume pure pp_app_core through App::request_close, App::save_document, App::continue_document_workflow_after_optional_save, pano_cli simulate-app-session, pano_cli plan-document-session-prompt, DocumentSaveServices, CloseRequestServices, DocumentWorkflowServices, and src/legacy_document_session_services.*; close/save-before prompt creation now uses src/legacy_app_dialog_services.*, Save dialog working-directory picker visibility/path formatting now dispatches through PlatformServices, and existing-project save/save-before-workflow execution prepares a payload-bearing canvas snapshot report before retained saving, but the bridge still opens retained save dialogs, wires prompt callbacks directly, delegates actual writing to Canvas::project_save, mutates the unsaved flag on close confirmation, invokes native app close, and routes save-version through the retained legacy dialog Preserve current close/save/dirty-workflow behavior while document session execution moves toward app/document/UI/platform services pp_app_core_document_session_tests; pp_platform_api_tests; pano_cli plan-document-session-prompt --kind close-unsaved; pano_cli plan-document-session-prompt --kind save-before-workflow; pano_cli simulate-app-session --unsaved --save-intent save-dirty-version; pano_cli simulate-app-session --no-canvas; pano_cli plan-document-file --work-dir D:/Paint --name demo --target-exists; pano_cli plan-document-version --directory D:/Paint --doc-name demo.01 --existing-path D:/Paint/demo.02.ppi; ctest --preset desktop-fast --build-config Debug Close prompt execution, native close requests, dirty-workflow save prompts, existing-project saves, save dialogs, save-version execution, and unsaved-flag mutation are owned by injected app/document/UI/platform services with App methods acting only as adapters
DEBT-0041 Open Modernization Accepted new-document planning/execution dispatch and new-document overwrite prompt metadata now consume pure pp_app_core through App::dialog_newdoc, pano_cli plan-new-document, pano_cli plan-document-session-prompt, NewDocumentServices, and src/legacy_document_session_services.*; new-document overwrite prompt creation now uses src/legacy_app_dialog_services.*, and New Document dialog working-directory picker visibility/path formatting now dispatches through PlatformServices, but the bridge still mutates legacy app document fields, clears legacy layer UI, resizes legacy Canvas, clears legacy history, creates the default layer through legacy UI, mutates unsaved/new-document flags, updates the title, wires overwrite callbacks directly, and handles keyboard/dialog cleanup directly Preserve current New Document dialog behavior while document creation moves toward app/document/UI services pp_app_core_document_session_tests; pp_platform_api_tests; pano_cli plan-new-document --work-dir D:/Paint --name demo --resolution-index 3; pano_cli plan-new-document --work-dir D:/Paint --name demo --resolution-index 3 --target-exists; pano_cli plan-document-session-prompt --kind new-document-overwrite; pano_cli simulate-app-session --save-intent save; ctest --preset desktop-fast --build-config Debug New document creation, overwrite confirmation, canvas/document allocation, default layer creation, history clearing, title updates, dirty/new-document state, and keyboard/dialog cleanup are owned by injected app/document/UI services with App::dialog_newdoc acting only as a UI adapter
DEBT-0042 Open Modernization Accepted Save As and Save Version planning/execution dispatch plus Save As overwrite prompt metadata now consumes pure pp_app_core through App::dialog_save, App::dialog_save_ver, pano_cli plan-document-file, pano_cli plan-document-version, pano_cli plan-document-session-prompt, DocumentFileSaveServices, DocumentVersionSaveServices, and src/legacy_document_session_services.*; Save As overwrite prompt creation now uses src/legacy_app_dialog_services.*, and accepted Save As/Save Version execution prepares a payload-bearing canvas snapshot report before retained saving, but the bridge still wires overwrite callbacks directly, delegates actual writing to legacy Canvas::project_save, mutates app document name/path/directory fields, marks version saves dirty before saving, updates the title, and handles keyboard/dialog cleanup directly Preserve current Save As and Save Version behavior while document persistence moves toward app/document/storage/UI services pp_app_core_document_session_tests; pano_cli plan-document-file --work-dir D:/Paint --name demo --target-exists; pano_cli plan-document-session-prompt --kind file-overwrite --name demo; pano_cli plan-document-version --directory D:/Paint --doc-name demo.01 --existing-path D:/Paint/demo.02.ppi; pano_cli simulate-app-session --save-intent save-as; pano_cli simulate-app-session --save-intent save-version; ctest --preset desktop-fast --build-config Debug Save As overwrite prompting, project-save execution, app document metadata updates, title updates, version-save dirty-state handling, and keyboard/dialog cleanup are owned by injected app/document/storage/UI services with App::dialog_save and App::dialog_save_ver acting only as UI adapters
DEBT-0043 Open Modernization Equirectangular, layer, animation-frame, depth, and cube-face export planning/execution dispatch now consumes pure pp_app_core through App::dialog_export, App::dialog_export_layers, App::dialog_export_anim_frames, App::dialog_export_depth, App::dialog_export_cube_faces, pano_cli plan-export-*, DocumentExportServices, and src/legacy_document_export_services.*; layer/frame dialogs also consume plan_document_export_collection_target plus PlatformServices::uses_work_directory_document_export_collections() instead of spelling local iOS branches, and export success/failure/license dialog metadata plus execution log labels now come from pp_app_core, but the bridge still calls legacy Canvas export methods, creates export directories, handles picker-selected stems, and performs Web prepared-file handoff directly Preserve current image/collection/depth/cube export behavior while export execution moves toward document/renderer/platform/storage services pp_app_core_document_export_tests; pp_platform_api_tests; pano_cli plan-export-start --requires-license --demo; pano_cli plan-export-menu --kind layers; pano_cli plan-export-target --kind collection --work-dir D:/Paint --doc-name demo --suffix _layers; pano_cli plan-export-message --kind equirectangular --destination work --detail D:/Paint; pano_cli plan-export-report --kind license-disabled; pano_cli simulate-document-export; ctest --preset desktop-fast --build-config Debug File, collection, stem, depth, and cube export execution, export-directory creation, Web file handoff, picker-selected stem handling, and legacy canvas export calls are owned by injected document/renderer/platform/storage services with export dialogs acting only as UI adapters
DEBT-0044 Open Modernization Timelapse and animation MP4 export execution dispatch now consumes pure pp_app_core through App::dialog_timelapse_export, App::dialog_export_mp4, pano_cli plan-export-menu, pano_cli plan-export-target --kind name, pano_cli plan-export-message, pano_cli plan-export-report, DocumentVideoExportServices, and src/legacy_document_export_services.*, and success/failure/license dialog metadata plus execution log labels now come from pp_app_core, but the bridge still launches legacy desktop timelapse worker threads, calls App::rec_export, calls Canvas::export_anim_mp4, and owns mobile/Web save callbacks Preserve current MP4/timelapse export behavior while video export moves toward app/document/renderer/video/platform/storage services pp_app_core_document_export_tests; pano_cli plan-export-menu --kind animation-mp4; pano_cli plan-export-menu --kind timelapse; pano_cli plan-export-target --kind name --doc-name demo --suffix -animation; pano_cli plan-export-target --kind name --doc-name demo --suffix -timelapse; pano_cli plan-export-message --kind timelapse --destination success; pano_cli plan-export-report --kind animation-mp4 --message "video export path must not be empty"; ctest --preset desktop-fast --build-config Debug Timelapse and animation MP4 execution, desktop worker threading, frame readback/video encoding handoff, and mobile/Web save callbacks are owned by injected app/document/renderer/video/platform/storage services with export dialogs acting only as UI adapters
DEBT-0045 Open Modernization Options-menu preference execution now consumes pure pp_app_core through UI scale, viewport scale, RTL direction, VR mode, VR-controller, auto-timelapse, and canvas cursor-mode callbacks plus AppPreferenceServices and src/legacy_app_preference_services.*; viewport-density and cursor-mode execution now delegate to src/legacy_canvas_view_services.*, but the bridges still call legacy App::set_ui_scale, App::set_ui_rtl, App::rec_start, App::rec_stop, retained canvas view mutation, and Settings::save directly; VR mode callbacks now call App VR wrappers that dispatch to PlatformServices, whose desktop runtime policy prefers OpenXR while the actual Windows OpenVR SDK bridge still lives in WindowsPlatformServices under DEBT-0061 Preserve current options-menu behavior while preferences move toward app/UI/platform/storage services pp_app_core_app_preferences_tests; pp_app_core_canvas_view_tests; pano_cli plan-app-preferences --ui-scale 1.5 --display-density 2 --current-scale 1.6 --scale-option 1 --scale-option 1.5 --rtl; pano_cli plan-canvas-view-density --density 1.5; pano_cli plan-canvas-view-cursor-mode --mode 3; ctest --preset desktop-fast --build-config Debug; cmake --build --preset windows-msvc-default --config Debug --target PanoPainter Preference persistence, UI/layout direction, viewport density, cursor mode, VR mode start/stop/failure handling, VR-controller state, and auto-timelapse recording side effects are owned by injected app/UI/platform/storage services with options-menu callbacks acting only as UI adapters
DEBT-0046 Open Modernization Startup preference/runtime execution and startup resource sequencing now consume pure pp_app_core through App::init, pano_cli plan-app-startup, pano_cli plan-app-startup-resources, AppStartupServices, AppStartupResourceServices, and src/legacy_app_startup_services.*, but the bridge still calls legacy Settings::set, Settings::save, App::rec_start, app VR-controller state mutation, message-box license warning execution, shader loading, asset initialization, layout creation, title updates, and UI render-target creation directly Preserve current startup behavior while app startup moves toward app/preferences/storage/recording/UI/renderer services pp_app_core_app_startup_tests; pano_cli plan-app-startup --run-counter 7 --vr-controllers-disabled --license-invalid; pano_cli plan-app-startup --run-counter -1; pano_cli plan-app-startup-resources --width 1280 --height 720; pano_cli plan-app-startup-resources --bad-size; ctest --preset desktop-fast --build-config Debug; cmake --build --preset windows-msvc-default --config Debug --target PanoPainter Startup preference persistence, auto-timelapse startup, stored VR-controller state, license validation/warning, startup resource initialization, title updates, and UI render-target allocation are owned by injected app/preferences/storage/recording/UI/renderer services with App::init acting only as orchestration
DEBT-0047 Open Modernization PPBR brush package export request validation, success-dialog metadata, and execution dispatch now consume pure pp_app_core through App::dialog_ppbr_export, pano_cli plan-brush-package-export, BrushPackageExportServices, and src/legacy_brush_package_export_services.*; PPBR header/path planning now consumes pp_assets::brush_package, and the macOS data-directory override now routes through PlatformServices, but the bridge still reads NodeDialogExportPPBR, carries the legacy Image header object outside the pure request, converts to NodePanelBrushPreset::PPBRInfo, calls NodePanelBrushPreset::export_ppbr, owns desktop worker-thread dispatch, dialog destruction, and mobile/Web completion directly Preserve current PPBR export behavior while brush assets, PPBR serialization, picker completion, and UI lifetime move toward asset/storage/UI/platform services pp_assets_brush_package_tests; pp_app_core_brush_package_export_tests; pp_platform_api_tests; pano_cli plan-brush-package-export --path D:/Paint/clouds.ppbr --author Artist --dest-path D:/Paint/BrushPreviews --export-data --header-image; pano_cli plan-brush-package-export --path D:/Paint/clouds.ppbr; pano_cli plan-brush-package-export; pano_cli plan-brush-package-export --path clouds; pano_cli plan-brush-package-export --path D:/Paint/clouds.ppbr --dest-path D:/Paint/BrushPreviews --no-export-data; ctest --preset desktop-fast --build-config Debug; cmake --build --preset windows-msvc-default --config Debug --target PanoPainter PPBR metadata collection, header-image ownership, serialization, picker-selected path execution, desktop threading, dialog lifetime, and mobile/Web completion are owned by injected brush asset/storage/UI/platform services with App::dialog_ppbr_export acting only as a UI adapter
DEBT-0048 Open Modernization ABR/PPBR brush package import execution now consumes pure pp_app_core through document-open confirmation callbacks, pano_cli plan-brush-package-import, BrushPackageImportServices, and src/legacy_brush_package_import_services.*; imported brush tip/pattern target paths now consume pp_assets::brush_package, but the bridge still launches detached legacy NodePanelBrushPreset::import_abr/import_ppbr worker threads and depends on the legacy preset panel as the importer/storage owner Preserve current brush import behavior while brush package parsing, preset storage, progress/error reporting, and UI refresh move toward asset/paint/UI services pp_assets_brush_package_tests; pp_app_core_brush_package_import_tests; pano_cli plan-brush-package-import --kind ppbr --path D:/Paint/Brushes/clouds.ppbr; pano_cli plan-brush-package-import --kind abr --path D:/Paint/Brushes/clouds.abr; pano_cli plan-brush-package-import --kind ppbr; ctest --preset desktop-fast --build-config Debug; cmake --build --preset windows-msvc-default --config Debug --target PanoPainter ABR/PPBR parsing, preset creation/storage, import threading/progress, duplicate asset policy, and UI refresh are owned by injected brush asset/paint/UI services with document-open callbacks only confirming user intent
DEBT-0049 Open Modernization pp_assets::validate_ppbr_header intentionally preserves the legacy PPBR version check from NodePanelBrushPreset::import_ppbr, which accepts files when either major is 0 or minor is 1 instead of requiring exactly version 0.1 Avoid rejecting existing brush packages before compatibility fixtures prove the stricter rule is safe pp_assets_brush_package_tests; pano_cli plan-brush-package-export --path D:/Paint/clouds.ppbr; ctest --preset desktop-fast --build-config Debug; cmake --build --preset windows-msvc-default --config Debug --target PanoPainter Add PPBR compatibility fixtures for accepted/rejected historical package versions, then require canonical 0.1 or an explicit supported-version matrix and update live import accordingly
DEBT-0050 Open Modernization iOS exported-image photo-library publishing and WebGL persistent-storage flushing now dispatch through PlatformServices; the iOS/Web policy decision lives in tested pp_platform_api::platform_policy, but non-Windows execution still lives in src/platform_legacy/legacy_platform_services.* and forwards to retained save_image_library/webgl_sync bridges Preserve current iOS/Web export and save behavior while the Apple/Web platform shells are extracted incrementally pp_platform_api_tests; ctest --preset desktop-fast --build-config Debug; platform package smoke once Apple/Web root builds exist Exported-image publishing and persistent-storage flushing are owned by injected Apple/Web pp_platform_* services with no legacy adapter branch
DEBT-0051 Open Modernization Document browser search roots and Browse dialog working-directory picker visibility/path formatting now dispatch through PlatformServices; iOS Inbox roots and working-directory picker availability live in tested pp_platform_api::platform_policy, but macOS directory picker/display-path execution still lives in src/platform_legacy/legacy_platform_services.* Preserve current iOS document import/browse and desktop browse picker behavior while Apple platform shells are extracted incrementally pp_platform_api_tests; ctest --preset desktop-fast --build-config Debug; Apple package smoke once root Apple builds exist Document browse roots and browse-directory picker/display formatting are owned by injected Apple and desktop pp_platform_* services with no legacy adapter branch
DEBT-0052 Open Modernization Native UI/window state saving now dispatches through PlatformServices; Windows/macOS save policy lives in tested pp_platform_api::platform_policy, but macOS execution still lives in src/platform_legacy/legacy_platform_services.* and forwards to the retained Objective-C app bridge Preserve current Windows/macOS UI persistence while platform shells are extracted incrementally pp_platform_api_tests; ctest --preset desktop-fast --build-config Debug; Windows app build; Apple package smoke once root Apple builds exist UI/window state persistence is owned by injected platform services with no legacy adapter branch
DEBT-0053 Open Modernization Prepared-file writable target selection and prepared-file export-dialog policy now dispatch through PlatformServices; iOS temporary-file and WebGL data-path target planning live in tested pp_platform_api::platform_policy, but retained iOS/Web save/download handoff execution still lives in src/platform_legacy/legacy_platform_services.* Preserve mobile/Web export handoff behavior while platform shells are extracted incrementally pp_platform_api_tests; ctest --preset desktop-fast --build-config Debug; Windows app build; Apple/Web package smoke once root package builds exist Prepared-file target selection, export-dialog policy, and save/download handoff are owned by injected platform services with no legacy adapter branch
DEBT-0054 Open Modernization Layout XML file read/reload decisions now consume pp_platform_api::plan_asset_file_load; platform-family reload behavior lives in tested pp_platform_api::platform_policy and pure probed planning, but the live wrapper still performs direct stat probing for Windows/macOS mtime reload checks until platform storage/file-watch services exist Preserve current layout hot-reload and mobile/Web single-load behavior while removing platform guards from the shared LayoutManager parser pp_platform_api_tests; ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests; Windows app build Layout reload decisions are owned by injected platform storage/file-watch services or an asset manager boundary with platform-specific file watching removed from compile-time helpers
DEBT-0055 Open Modernization src/app.h now forward-declares retained iOS/macOS/Android/Linux/Web platform handles instead of including platform SDK headers, and full SDK includes are isolated in src/platform_legacy/legacy_platform_services.cpp, but the App singleton still stores those platform handles directly Reduce central header platform coupling incrementally without rewriting non-Windows platform entrypoints before Phase 6 Windows app build; Apple/Android/Linux/Web package smoke once platform root builds are active Platform handles are owned by injected pp_platform_* shell state or services, and App has no platform SDK handle fields or platform conditional members
DEBT-0056 Open Modernization src/asset.h is now Android-SDK-free and uses opaque Android asset handles behind Asset::set_android_asset_manager, but retained Asset still owns a static Android asset-manager bridge and src/asset.cpp still performs Android AAssetManager reads directly; the current android-arm64 root preset is headless and does not expose pp_legacy_assets_io, though the retained Android standard package native-lib now builds through its refreshed C++23 CMake path Reduce legacy asset I/O header coupling without rewriting Android asset loading before the asset manager/storage boundary exists Windows app build; powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64 -Targets pp_assets; powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages standard Android asset loading is owned by injected asset storage/platform services or pp_assets file providers, with no static Android asset manager on Asset
DEBT-0060 Open Modernization Retained Android package CMake generates a patched nanort.h overlay in the build tree for native-lib instead of modifying the libs/nanort submodule Current SDK Manager NDK/Clang rejects TriangleSAHPred::operator= assigning to a const size_t member, but the retained grid/lightmap path still includes nanort before that dependency is replaced or updated powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages standard; Quest/Focus retained package configure checks; Windows app build Update/replace nanort, move grid/lightmap baking behind a component that owns its dependency, or retire the retained Android package CMake path so no generated vendor overlay is required
DEBT-0061 Open Modernization Desktop XR runtime selection now lives in tested pp_platform_api policy and prefers OpenXR, but WindowsPlatformServices still reports OpenXR unavailable and reaches the retained OpenVR SDK bridge as a legacy fallback; Windows runtime deployment copies openvr_api.dll beside PanoPainter.exe until that fallback is removed Preserve current desktop VR behavior while replacing OpenVR with OpenXR behind the platform/renderer boundary pp_platform_api_tests; ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests --output-on-failure; cmake --build --preset windows-msvc-default --config Debug --target PanoPainter Add an OpenXR SDK/package target, implement desktop OpenXR startup/shutdown/pose/controller submission behind pp_platform_vr or PlatformServices, validate parity with mocked/runtime smoke coverage, and remove libs/openvr plus the OpenVR link/include paths from root CMake
DEBT-0062 Open Modernization VS 2026 builds generate a patched fmt format.h overlay in the build tree for pp_legacy_vendor, disabling the old _SECURE_SCL checked-array iterator branch while leaving the fmt submodule clean VS 2026's STL no longer exposes the legacy checked-array iterator used by this old fmt release, but replacing fmt is part of the dependency migration rather than this platform unblock cmake --build --preset windows-msvc-default --config Debug --target PanoPainter; cmake --build --preset windows-msvc-default --config Debug --target pp_platform_api_tests; ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests --output-on-failure Move fmt to a supported vcpkg/package version or update the vendored fmt release, then remove the generated fmt overlay from pp_legacy_vendor
DEBT-0057 Open Modernization Default canvas allocation size now dispatches through PlatformServices::default_canvas_resolution, removing the CANVAS_RES platform macro from src/canvas.h; WebGL's retained 512 default now lives in tested pp_platform_api::platform_policy, but the Web shell still reaches it through the legacy platform fallback until injected Web services own the policy Preserve WebGL memory behavior while moving canvas creation policy out of shared canvas headers and into the platform boundary pp_platform_api_tests; ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests; Windows app build; WebGL package smoke once root Web build exists Default canvas resolution is owned by injected pp_platform_* services for every supported platform, with no WebGL branch in the legacy fallback
DEBT-0058 Open Modernization App-level progress/message/input dialog metadata, including message-dialog OK/cancel captions, now consumes pure pp_app_core through App::show_progress, App::message_box, App::input_box, pano_cli plan-app-dialog, and pp_app_core_app_dialog_tests; live execution is centralized in src/legacy_app_dialog_services.*, but the bridge still creates retained NodeProgressBar, NodeMessageBox, and NodeInputBox instances and inserts them into the legacy layout tree Preserve current app-shell dialog behavior while moving shared dialog policy toward UI/app services pp_app_core_app_dialog_tests; pano_cli plan-app-dialog --kind progress --total -4; pano_cli plan-app-dialog --kind message --cancel; pano_cli plan-app-dialog --kind input --ok-caption Save; ctest --preset desktop-fast --build-config Debug; Windows app build Progress/message/input dialog creation, callback wiring, layout insertion, lifetime ownership, and headless automation are owned by injected app/UI services with App methods acting only as adapters
DEBT-0059 Open Modernization iOS root CMake headless builds assign generated bundle identifiers and disable code signing for executable test/tool targets The current Apple gate is compile validation for shared component targets; signed iOS app/package validation is not migrated to root CMake yet powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.ps1 -Presets macos,ios-simulator,ios-device; sh scripts/automation/platform-build.sh "ios-device" on panopainter-mac Root CMake owns the signed Apple app/package targets, package-smoke validates Apple bundles where signing material is available, and headless iOS test/tool targets are either excluded from signed package builds or use explicit test-runner signing policy

Closed Debt

ID Status Owner Item Reason Validation Removal Condition
DEBT-0006 Closed Modernization pano_cli create-document validates and emits JSON command contracts but does not yet invoke the legacy document/app model The document model had not been extracted from Canvas/App yet ctest --preset desktop-fast --build-config Debug; pano_cli_create_document_smoke Closed on 2026-05-31: command now constructs a real pp_document::CanvasDocument
DEBT-0018 Closed Modernization pp_renderer_gl owned a tested OpenGlInitialState plan for PanoPainter startup depth/blend policy, but App::init still executed the plan through direct OpenGL calls Preserve behavior while moving renderer policy into the backend boundary before a live IRenderDevice/command context owns startup execution pp_renderer_gl_capabilities_tests; ctest --preset desktop-fast --build-config Debug; powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug Closed on 2026-06-03: pp_renderer_gl::apply_panopainter_initial_state now applies the startup state through a tested backend dispatch contract consumed by App::init