71 KiB
71 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.ps1andplatform-build.shnow include the current headless component/test matrix, including brush-package coverage and the app-core startup/file/document/ brush/canvas/history/grid/toolbar/tools/about/preferences/status automation tests, andpanopainter_platform_build_target_matrix_self_testnow 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.ps1andpackage-smoke.shnow have readiness-only modes that report the same Windows AppX, Android standard/Quest/Focus APK, Apple bundle, and WebGL blocker matrix without requiring an app build first, andpanopainter_package_smoke_readiness_self_testguards package-kind parity across both wrappers. Package target migration remains open. - 2026-06-05: DEBT-0022 was narrowed.
pp_app_corenow owns tested onion-skin frame range and alpha falloff planning, and liveNodeCanvaspanorama 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 testedpp_app_coreplanning withpano_cli plan-animation-timeline-scrubcoverage, soNodeAnimationTimelineno longer owns cursor-to-frame clamp policy. Animation panel layer/frame view projection now also uses a testedpp_app_coreview model exposed bypano_cli plan-animation-panel-view, including stale-selection behavior. Legacy canvas/layer/UI execution remains open under DEBT-0022. - 2026-06-05: DEBT-0021 was narrowed again. Layer panel selected-control and
visibility view projection now goes through tested
pp_app_coreplanning,NodePanelLayer::update_attributes()consumes that view model in the live app, andpano_cli plan-layer-panel-viewexposes the same path for automation. Legacy layer mutation, UI node ownership, and undo wiring remain open under DEBT-0021. - 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_glcapability-state dispatch; CanvasLayer equirect export now binds cube textures through testedpp_renderer_gldispatch; andFonttext drawing now activates texture units through testedpp_renderer_gldispatch. Desktop VR drawing also now routes active texture unit switches and fallback 2D texture unbinds through testedpp_renderer_gldispatch. The debt remains open for live stroke rasterization, dual-brush compositing, pattern feedback math, thumbnail layer compositing, brush-preview compositing, and retainedShaderManager::ext_*compatibility fields.
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 | 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, 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 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 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, 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-recording-session, pano_cli plan-app-preferences, pano_cli plan-app-status, 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/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, status/display UI rendering, renderer diagnostic capability adaptation, 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 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_preferences_tests; pp_app_core_app_status_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-recording-session --running --frame-count 12; 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-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, WebGL, 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; 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, 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 | $env:VCPKG_ROOT="C:\Program Files\Microsoft Visual Studio\2022\Community\VC\vcpkg"; cmake --preset windows-msvc-vcpkg-headless; ctest --preset desktop-fast-vcpkg --build-config Debug |
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 preset is used for local validation because the VS 2026 generator is not installed here |
The target VS 2026 preset must remain, but this machine configures with Visual Studio 17 2022 | cmake --preset windows-msvc-default; ctest --preset desktop-fast --build-config Debug |
Validate windows-vs2026-x64 on a machine with Visual Studio 2026 installed and make it the default Windows validation preset |
| DEBT-0009 | Open | Modernization | Android root CMake validation currently builds headless targets only, not APK/package variants; standard arm64/x64, Quest arm64, and Focus/Wave arm64 have named root build presets and share the refreshed platform-build component matrix |
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 |
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, and stroke-script-to-face-payload CLI automation, but it is not yet wired to legacy Canvas, legacy save, or legacy action commands |
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; pp_document_tests; pp_document_ppi_import_tests; pp_document_ppi_export_tests; pano_cli_simulate_document_edits_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 |
Legacy document behavior is represented by pp_document tests and the app consumes it through a boundary/facade |
| DEBT-0011 | Open | Modernization | package-smoke validates the Windows CMake app artifact and now reports a structured package readiness matrix for Windows AppX, Android standard/Quest/Focus APKs, Apple bundles, and WebGL output, but those 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; bash -n scripts/automation/package-smoke.sh |
Package-smoke builds and validates Windows AppX, Android APK variants, 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, and decoded pixel attachment to pp_document, but full legacy PPI round-trip parity is 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, and execution dispatch now consume pure pp_app_core through App::init_sidebar, NodePanelBrush, NodePanelBrushPreset, NodePanelStroke, restored/docked floating-panel callbacks, pano_cli plan-brush-operation, pano_cli plan-brush-texture-list, pano_cli plan-brush-preset-list, pano_cli plan-brush-stroke-control, 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 still mutates legacy Brush/Canvas::I, loads/saves legacy brush texture images, refreshes legacy quick/stroke/color widgets, and uses temporary NodePanelBrush/NodePanelBrushPreset friend adapters to reach private list state |
Preserve existing brush UI behavior while brush commands 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-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; ctest --preset desktop-fast --build-config Debug |
Brush color/texture/preset/stroke-settings, texture-list, preset-list, and stroke-control execution are owned by injected brush/app/asset/UI services with no legacy brush/canvas adapter, direct NodePanelBrushPreset child mutation, 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 and mini-state planning and execution dispatch now consume pure pp_app_core through NodePanelQuick, pano_cli plan-quick-operation, and the QuickUiServices boundary; live execution is centralized in src/legacy_quick_ui_services.*, but the bridge still mutates legacy quick UI widgets, Brush previews, color picker popup state, and preset popup state |
Preserve quick-panel behavior while quick brush/color 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; ctest --preset desktop-fast --build-config Debug |
Quick-panel selection, popup, restore, reset, brush preview, and color execution are owned by injected app/brush/UI services with no legacy quick-panel adapter |
| 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, canvas input mode switching, active-state planning/execution dispatch, 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-state, pano_cli plan-canvas-hotkey, CanvasToolServices, and CanvasHotkeyServices, live toolbar/input/hotkey execution is centralized in src/legacy_canvas_tool_services.*, and canvas mode tip visibility plus pressure remapping now route through PlatformServices, but the bridge still mutates or reads legacy Canvas mode state, pen picking state, touch-lock state, transform copy/cut action objects, ActionManager, legacy save UI, legacy stroke size controls, and cursor/UI singletons |
Preserve current toolbar, stylus eraser, 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-state --mode draw --picking --touch-lock; 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 state refresh, picking, touch lock, stylus eraser/key mode switching, 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, and the ToolsMenuServices boundary, and direct command execution is centralized in src/legacy_app_shell_services.*; SonarPen availability/startup now routes through PlatformServices, but live adapters still construct legacy NodePanelFloating panels, mutate legacy panel nodes, clear CanvasModeGrid, reset NodeCanvas camera state, open legacy shortcuts UI, and rely on the legacy platform adapter for the retained iOS SonarPen bridge |
Preserve current Tools menu behavior while UI shell actions move toward app/UI/platform services | pp_app_core_tools_menu_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; ctest --preset desktop-fast --build-config Debug |
Tools panel creation, submenu routing, grid clear, camera reset, shortcuts dialog, and SonarPen dispatch are owned by injected app/UI/platform services with App::init_menu_tools acting only as a UI adapter and no legacy Tools 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, 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/message-box dialogs 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 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. 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 route blend state, active texture units, viewport execution, color clears, and cube-face framebuffer-to-texture copies through tested pp_renderer_gl dispatch adapters, 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, 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, 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 route viewport query, clear-color query, color-buffer clear, and clear-color restore through tested pp_renderer_gl dispatch helpers, and retained Canvas plus CanvasLayer stroke/object/thumbnail/frame-clear draw-state paths now route saved viewport or 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, 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; 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 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.*, 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 UI, 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-clears-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 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, and the CloudServices boundary; live execution is centralized in src/legacy_cloud_services.*, the app-owned upload/download/license curl helpers now ask PlatformServices for the Android TLS-verification bypass policy, 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, progress/message UI, 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; 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, and save-before-workflow planning/execution dispatch 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, DocumentSaveServices, CloseRequestServices, DocumentWorkflowServices, and src/legacy_document_session_services.*; Save dialog working-directory picker visibility/path formatting now dispatches through PlatformServices, but the bridge still opens legacy message boxes/save dialogs, calls Canvas::I->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 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 now consumes pure pp_app_core through App::dialog_newdoc, pano_cli plan-new-document, NewDocumentServices, and src/legacy_document_session_services.*; 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, 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 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 now consumes pure pp_app_core through App::dialog_save, App::dialog_save_ver, pano_cli plan-document-file, pano_cli plan-document-version, DocumentFileSaveServices, DocumentVersionSaveServices, and src/legacy_document_session_services.*, but the bridge still opens legacy overwrite prompts, calls 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-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, but the bridge still calls legacy Canvas export methods, owns platform-specific export success messages, 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 simulate-document-export; ctest --preset desktop-fast --build-config Debug |
File, collection, stem, depth, and cube export execution, export-directory creation, platform success reporting, 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, DocumentVideoExportServices, and src/legacy_document_export_services.*, but the bridge still launches legacy desktop timelapse worker threads, calls App::rec_export, calls Canvas::export_anim_mp4, owns mobile/Web save callbacks, and emits success messages directly |
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; ctest --preset desktop-fast --build-config Debug |
Timelapse and animation MP4 execution, desktop worker threading, frame readback/video encoding handoff, mobile/Web save callbacks, and success reporting 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.*, but the bridge still calls legacy App::set_ui_scale, App::set_ui_rtl, NodeCanvas::set_density, NodeCanvas::set_cursor_visibility, App::rec_start, App::rec_stop, and Settings::save directly; its VR mode callbacks now call App VR wrappers that dispatch to PlatformServices, while the actual Windows OpenVR SDK bridge still lives in WindowsPlatformServices |
Preserve current options-menu behavior while preferences move toward app/UI/platform/storage services | pp_app_core_app_preferences_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; 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 now consumes pure pp_app_core through App::init, pano_cli plan-app-startup, AppStartupServices, and src/legacy_app_startup_services.*, but the bridge still calls legacy Settings::set, Settings::save, App::rec_start, app VR-controller state mutation, and message-box license warning execution directly |
Preserve current startup behavior while app startup moves toward app/preferences/storage/recording/UI 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; 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, and startup UI/runtime side effects are owned by injected app/preferences/storage/recording/UI services with App::init acting only as orchestration |
| DEBT-0047 | Open | Modernization | PPBR brush package export request validation 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, mobile/Web completion, and success-message behavior 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; 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 success UI 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, 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, but iOS Inbox inclusion and macOS directory picker/display-path behavior still live 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, 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, but iOS/Web target selection 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, but that helper still encodes the retained compile-time platform policy: Windows/macOS use stat mtime reload checks, while other platforms treat already-loaded layouts as successful no-op loads |
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 now forward-declares Android asset-manager types and uses Asset::set_android_asset_manager instead of public mutable manager state, but retained Asset still stores Android asset handles 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 |
Reduce legacy asset I/O header coupling without rewriting Android asset loading before the asset manager/storage boundary exists | Windows app build; cmake --build --preset android-arm64 --target pp_assets; Android package smoke once package builds consume shared targets |
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-0057 | Open | Modernization | Default canvas allocation size now dispatches through PlatformServices::default_canvas_resolution, removing the CANVAS_RES platform macro from src/canvas.h, but WebGL's retained 512 default still lives in src/platform_legacy/legacy_platform_services.cpp until the Web shell owns injected services |
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 |
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 |