80 KiB
Build And Platform Inventory
Status: live Last updated: 2026-06-05
This inventory records the known build surfaces during the CMake migration. Keep it updated as platform paths move to shared CMake targets.
Existing Build Entrypoints
| Platform/Target | Current Entrypoint | Notes |
|---|---|---|
| Windows desktop | Root CMakeLists.txt, preset windows-msvc-default; target preset windows-vs2026-x64 retained for VS 2026 |
Raw .sln/.vcxproj files removed on 2026-05-31; local machine currently uses Visual Studio 17 2022; PanoPainter now links through pp_platform_windows and panopainter_app, with Windows/vendor link dependencies owned by the platform shell, runtime payload deployment in cmake/PanoPainterRuntime.cmake, tested app-level document-open routing plus open/close/save session decisions owned by pp_app_core, SDK-free clipboard/cursor/virtual-keyboard/display/share/picker service contracts owned by pp_platform_api, and injected WindowsPlatformServices now isolated in src/platform_windows/windows_platform_services.*; retained third-party source dependencies contained by pp_legacy_vendor, retained asset/file/serialization sources contained by pp_legacy_assets_io, retained paint/document/canvas sources contained by pp_legacy_paint_document, retained OpenGL runtime sources contained by pp_legacy_renderer_gl and folded into pp_legacy_engine, retained runtime shell sources contained by pp_legacy_engine, retained base UI controls contained by pp_legacy_ui_core and folded into pp_legacy_app, app orchestration/version metadata owned by panopainter_app, and app-specific modal/dialog/panel/canvas workflow nodes owned by pp_panopainter_ui |
| Windows AppX | PanoPainterPackage/Package.appxmanifest, .wapproj referenced by solution |
Distribution packaging |
| macOS | PanoPainter-OSX/ project files and Info.plist |
Uses NSOpenGLView today |
| iOS | PanoPainter/Info.plist, related Apple sources |
Uses OpenGL ES today |
| Android standard | android/android/build.gradle, android/android/CMakeLists.txt |
Retained native library target native-lib; CMake 3.10/C++23 baseline now links the standard arm64 package path with modern component/service sources and the generated nanort overlay helper |
| Android Quest | android/quest/build.gradle, android/quest/CMakeLists.txt |
OVR SDK imported libraries; CMake 3.10/C++23 baseline and current Yoga source list configure with the shared Android package compatibility helper |
| Android Focus/Wave | android/focus/build.gradle, android/focus/CMakeLists.txt |
Wave SDK imported libraries; CMake 3.10/C++23 baseline and current Yoga source list configure with the shared Android package compatibility helper |
| Linux | linux/CMakeLists.txt |
Retained app target now uses CMake 3.10 and target-level C++23 while package/root target migration remains open |
| WebGL/Emscripten | webgl/CMakeLists.txt |
Retained WebGL app target now uses CMake 3.10 and target-level C++23 with retained WebGL2/Emscripten link flags |
Existing Version Generation
- Script:
scripts/pre-build.py - Output:
src/version.gen.h - Current behavior: derives version from git branch, latest tag, short hash, commit count, and configuration argument.
- Migration requirement: root CMake should call this script through a custom command and avoid unnecessary tracked-file churn where possible.
Existing Dependency Sources
Hybrid policy: migrate reliable packages to vcpkg and retain SDK/patched dependencies until each platform triplet is proven.
| Dependency | Current Source | Initial Policy |
|---|---|---|
| fmt | libs/fmt |
Move to vcpkg |
| GLM | libs/glm |
Move to vcpkg |
| tinyxml2 | libs/tinyxml2 |
Move to vcpkg |
| stb | libs/stb |
Move to vcpkg or interface target if package friction |
| CURL | libs/curl-win, libs/curl-android-ios |
Move to vcpkg where triplets work |
| SQLite | libs/sqlite3 |
Move to vcpkg |
| GLAD | libs/glad |
Move to vcpkg or generated backend target |
| Catch2 | none yet | Add through vcpkg |
| OpenVR | libs/openvr |
Retain initially |
| OVR Platform/Mobile | libs/ovr_platform, libs/ovr_mobile |
Retain initially |
| Wave SDK | libs/wave_sdk |
Retain initially |
| Wacom WinTab | libs/wacom |
Retain initially |
| AppCenter Apple | libs/appcenter-apple |
Retain initially |
| openh264/mp4v2/libyuv | libs/openh264, libs/mp4v2, libs/libyuv |
Retain initially |
| jpeg helpers | libs/jpeg |
Evaluate after image tests exist |
| poly2tri/nanort/base64/hash-library | libs/* |
Evaluate after component split |
Current Validation Commands
These commands are the current local baseline.
cmake --preset windows-msvc-default
cmake --build --preset windows-msvc-default --config Debug --target PanoPainter
ctest --preset desktop-fast --build-config Debug
ctest --preset fuzz --build-config Debug
ctest --preset stress --build-config Debug
powershell -ExecutionPolicy Bypass -File scripts\automation\test.ps1 -Preset desktop-fast -Configuration Debug
powershell -ExecutionPolicy Bypass -File scripts\automation\build.ps1 -Preset windows-msvc-default -Configuration Debug -Target pano_cli
cmake --build --preset windows-msvc-default --target panopainter_validate_shaders
powershell -ExecutionPolicy Bypass -File scripts\automation\analyze.ps1 -Preset windows-msvc-default -NoApp
$env:VCPKG_ROOT = "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\vcpkg"
cmake --preset windows-msvc-vcpkg-headless
powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets 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
cmake --preset android-arm64
powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64
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 -Preset windows-msvc-default -Configuration Debug
powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -ReadinessOnly
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_windows_app_package_smoke
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
cmake --build --preset windows-msvc-default --config Debug --target panopainter_platform_build_android_assets
cmake --fresh --preset windows-clangcl-asan
python scripts/dev/clangd_nav.py symbols --file src/app_core/brush_ui.h --name execute_brush
python scripts/dev/clangd_nav.py symbols --file src/app_core/brush_ui.h --name-regex "execute_.*preset"
python scripts/dev/clangd_nav.py symbols --file src/app_core/document_export.h --detail-regex "Export.*Plan"
python scripts/dev/clangd_nav.py definition --file src/node_panel_brush.cpp --line 511 --column 39
python scripts/dev/clangd_nav.py references --file src/app_core/brush_ui.h --line 783 --column 45 --path-regex "src[\\/]app_core"
python scripts/dev/clangd_nav.py self-test
python scripts/dev/check_platform_build_targets.py
python scripts/dev/check_package_smoke_readiness.py
powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.ps1 -Presets macos,ios-simulator,ios-device
Known local toolchain state:
- CMake: 4.0.0-rc4
- Local Visual Studio generator selected by CMake: Visual Studio 17 2022
- Bundled vcpkg:
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\vcpkg(vcpkg versionreports 2025-11-19) - External vcpkg:
D:\vcpkgis currently resolved byscripts/automation/platform-build.ps1whenVCPKG_ROOTis already set on this host; the wrapper falls back to bundled Visual Studio vcpkg roots when needed. - Android SDK:
C:\Users\omara\AppData\Local\Android\Sdk - Android NDK: latest SDK Manager package checked/installed/selected by
scripts/automation/android-sdk-env.ps1; currentlyC:\Users\omara\AppData\Local\Android\Sdk\ndk\30.0.14904198 - Android CMake: latest SDK Manager package checked/installed/selected by
scripts/automation/android-sdk-env.ps1; currentlyC:\Users\omara\AppData\Local\Android\Sdk\cmake\4.1.2 - clang-cl:
C:\Program Files\LLVM\bin\clang-cl.exereports 18.1.8, but the selected VS 2026-preview STL expects Clang 20 or newer; see DEBT-0014 before treatingwindows-clangcl-asanas a passing sanitizer gate. - clangd/clang-query/clang-tidy are installed under
C:\Program Files\LLVM\bin.scripts/dev/clangd_nav.pyprovides agent-friendly symbol, definition, declaration, implementation, reference, and hover lookup through clangd using the currentcompile_commands.jsonfromwindows-clangcl-asan,android-arm64, or a caller-provided build directory. Symbol, hover, declaration, definition, and implementation lookups are the reliable use case. Symbol listing supports substring filtering with--name, regex filtering againstqualifiedNamewith--name-regex, and symbol detail/signature regex filtering with--detail-regex. Definition, declaration, implementation, and reference location output supports--path-regexfor path/URI filtering. Regex matching is case-insensitive by default and can be made case-sensitive with--no-ignore-case. The helper exposespython scripts/dev/clangd_nav.py self-test, also registered aspanopainter_clangd_nav_regex_self_test, so regex filter behavior can be validated without clangd or a compile database. Agents must use the repo skillpanopainter-code-navigationbefore broad text searches when a C++ refactor needs symbol identity, symbol-family lookup, generated-style names, signatures, override groups, declarations/definitions, or platform/backend path slices. Keeprgfor docs, build files, string literals, comments, and exact non-symbol text. Reference lookup is guarded because a one-shot clangd process may not have a complete project index: pass--background-indexfor broader best-effort results or--allow-incomplete-referencesfor explicitly current-translation-unit-only results, and do not treat incomplete references as proof that no other users exist. - Apple headless compile validation runs on the local Mac mini SSH alias
panopainter-macwith userbuilder. The host uses a Gitea SSH key forssh://git@git.omar.synology.me:3022/omar/panopainter.git, Homebrew CMake/Ninja/Git, and full Xcode selected per command withDEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer. The repeatable Windows-side command is:
powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.ps1 -Presets macos,ios-simulator,ios-device
The wrapper updates ~/Dev/panopainter, initializes the source submodules
required by the headless CMake matrix, and delegates to
scripts/automation/platform-build.sh. macOS, iOS simulator, and iOS device
currently validate shared component/test/tool targets only; signed Apple app
bundles remain tracked by DEBT-0011 and the iOS compile-only signing shortcut
is tracked by DEBT-0059.
- Android standard arm64/x64, Quest arm64, and Focus/Wave arm64 headless
configure/build pass through root CMake and the
platform-buildautomation wrapper defaults forpp_foundation,pp_assets,pp_paint,pp_document,pp_renderer_api,pp_renderer_gl,pp_paint_renderer,pp_ui_core,pano_cli, and their current headless test binaries, including foundation binary-stream/event/logging/task queue coverage, image metadata/decode/encode coverage, PPI header/layout/body coverage, settings document coverage, brush-package path/header coverage, document snapshot/frame/layer/history/PPI import-export coverage, paint brush/final-blend/stroke-alpha-blend/stroke spacing/stroke stress/ stroke-script coverage, renderer shader descriptor and OpenGL capability coverage, UI color/layout/XML parse coverage, platform service coverage, and the currentpp_app_coreautomation surface for startup, file menus, document session/open/import/export/animation/canvas/layer/resize/cloud/ sharing/platform-I/O, brush-package import/export, brush UI, canvas tools and hotkeys, history, grid, quick UI, main toolbar, Tools, About, preferences, and app status planning. The PowerShell wrapper accepts repeated, array, or comma-separated-Presets/-Targetsvalues so scripted callers can narrow the same matrix without accidentally passing a single combined target.scripts/dev/check_platform_build_targets.py, registered aspanopainter_platform_build_target_matrix_self_test, verifies the PowerShell and shell wrapper defaults include every current CMake test executable plus the required component andpano_clitargets, and that both wrappers default to the standard arm64/x64, Quest arm64, and Focus/Wave arm64 Android presets. It also guards the root CMake platform automation target names and the PowerShell/shell Android SDK Manager update hooks. package-smoke.ps1 -ReadinessOnlyandpackage-smoke.sh --readiness-onlyemit the Windows AppX, Android standard/Quest/Focus APK, Apple bundle, Linux app, and WebGL package readiness matrix without first building an app artifact. The full package smoke command still builds/checks the selected app target before reporting the same readiness matrix.package-smoke.ps1 -AndroidNativeChecksalso runs the retained Android standardnative-libbuild and Quest/Focus configure helper for selected Android package kinds, then reports those native results beside the APK blocker matrix.scripts/dev/check_package_smoke_readiness.py, registered aspanopainter_package_smoke_readiness_self_test, verifies both wrappers keep the same seven package kinds, blocked DEBT-0011 status, readiness-only mode, retained Android native-check prerequisite metadata, retained Linux/WebGL CMake baseline metadata, and root CMake package validation target names.- Root CMake exposes non-default package validation targets:
panopainter_package_readiness,panopainter_windows_app_package_smoke,panopainter_android_standard_native_package,panopainter_android_vr_native_package_configure, andpanopainter_android_native_package_smoke, andpanopainter_linux_webgl_package_readiness. These targets call the automation scripts from CMake but do not convert APK/AppX/Linux/Apple/WebGL package outputs to root CMake package targets yet. - Root CMake exposes non-default platform validation targets:
panopainter_platform_build_headless,panopainter_platform_build_android_assets,panopainter_platform_build_vcpkg_ui_core, andpanopainter_platform_build_apple_remote. These targets call the existing platform automation scripts from CMake and keep platform validation discoverable from the CMake target graph while app/package target migration remains open. - Retained Linux and WebGL app CMake entrypoints are still separate from root
CMake, but now share the modernization baseline of CMake 3.10 and
target-level
cxx_std_23.panopainter_retained_platform_cmake_self_testguards those baselines until these entrypoints are replaced by shared root CMake package/app targets. - Root CMake exposes named
fuzzandstressCTest presets.fuzzcurrently runs deterministic parser/serializer edge tests for binary streams, image metadata, PPI, stroke scripts, and layout XML;stresscurrently runs the stroke sampler stress coverage. pano_cli inspect-imagereports PNG IHDR metadata as JSON and is covered bypano_cli_inspect_png_metadata_smokewith a tiny IHDR fixture.pp_assets_image_pixels_testsdecodes PNG payloads, encodes RGBA8 pixels to PNG, round-trips encoded pixels back through the decoder, and rejects corrupt or malformed image payloads.pano_cli import-imageaccepts a PNG path, decodes RGBA8 pixels throughpp_assets, attaches them to a purepp_documentface payload, and is covered for checked-in decodable PNG import bypano_cli_import_image_smokeand metadata-valid truncated PNG rejection bypano_cli_import_image_rejects_truncated_png.pano_cli export-imagewrites a deterministic RGBA8 PNG throughpp_assetsand is covered bypano_cli_export_image_roundtrip_smoke, which imports the generated file back throughpano_cli import-image.pano_cli inspect-projectreports validated PPI thumbnail/body byte layout, body summary fields, layer/frame descriptors, and dirty-face PNG payload metadata, and is covered bypano_cli_inspect_project_layout_smokewith a minimal PPI fixture.pp_document_ppi_import_testsattaches decoded PPI dirty-face payloads topp_documentlayer/frame storage and rejects payloads outside document layers.pp_document_ppi_export_testsexports purepp_documentmetadata, per-layer frame durations, and RGBA8 face payloads to PPI bytes throughpp_assets, then decodes and reimports them for round-trip coverage.pano_cli simulate-document-exportexposes the same pure document-to-PPI export, asset-level decode, and document reimport path through JSON automation and is covered bypano_cli_simulate_document_export_smoke.pano_cli save-document-projectwrites that pure document export to a PPI file and is covered bypano_cli_save_document_project_roundtrip_smoke, which inspects and loads the generated file.pano_cli load-projectcreates app_documentprojection with per-layer frame counts, durations, and decoded face-pixel payloads when present; the metadata-only minimal fixture remains covered bypano_cli_load_project_metadata_smoke.pp_assets::create_ppi_projectwrites generated multi-layer, multi-frame PPI files with explicit per-layer names, opacity, blend mode, alpha lock, visibility, per-layer frame durations, and targeted dirty-face layer/frame payloads.pano_cli save-projectexposes that path for automation and is covered bypano_cli_save_project_roundtrip_smokeandpano_cli_save_project_payload_roundtrip_smoke, which reload generated metadata-only and targeted dirty-face-payload projects throughpano_cli load-project, pluspano_cli_save_project_rejects_non_finite_opacity, which verifies rejected automation floats do not create output files.pano_cli create-documentsupports--framesand--frame-duration-msand is covered bypano_cli_create_animation_document_smoke.pano_cli simulate-document-editsexercises pure document layer/frame edit operations, renderer-free face payloads, and renderer-free selection masks, and is covered bypano_cli_simulate_document_edits_smoke.pano_cli simulate-document-historyexercises pure document history apply/undo/redo behavior and is covered bypano_cli_simulate_document_history_smoke.src/legacy_document_layer_services.*is the current app-shell bridge for layer rename, layer menu clear/rename/merge, layer merge, and layer-panel operations. It keeps those live paths on thepp_app_corecontracts while legacyCanvas,NodeLayer,NodePanelLayer, andActionManagerexecution remain tracked byDEBT-0021andDEBT-0032.NodePanelLayer::update_attributes()now consumes the testedpp_app_corelayer panel view model for current opacity, alpha-lock, blend mode, and per-layer visibility projection.pano_cli plan-layer-panel-viewexposes the same view model for automation; retained canvas mutation, UI node ownership, and undo behavior remain tracked byDEBT-0021.src/legacy_app_shell_services.*is the current app-shell bridge for File menu routing, export-menu routing, main-toolbar commands, About menu commands, and direct Tools menu commands. It keeps those live paths on thepp_app_corecontracts while legacy dialogs, pickers, cloud/share/export, Tools, About, history, canvas-clear, settings, and platform SonarPen startup execution remain tracked byDEBT-0029,DEBT-0030,DEBT-0031,DEBT-0033,DEBT-0034, andDEBT-0035. The main-toolbar test message dialog metadata now lives inpp_app_coreand its retained creation routes throughsrc/legacy_app_dialog_services.*.src/legacy_canvas_view_services.*is the shared bridge for reset-camera, viewport-density, and cursor-mode execution; live Tools reset-camera, document open/new-document reset, cloud download reset, and options viewport/cursor callbacks consume the testedpp_app_corecanvas-view plans exposed throughpano_cli plan-canvas-camera-reset,pano_cli plan-canvas-view-density, andpano_cli plan-canvas-view-cursor-modebefore retained canvas mutation and settings writes.src/legacy_app_startup_services.*is the current main-app startup bridge for run-counter persistence, startup runtime side effects, and startup resource sequencing.App::initnow consumespp_app_coreplans exposed throughpano_cli plan-app-startupandpano_cli plan-app-startup-resources, while retained shader loading, asset initialization, layout creation, title updates, UI render-target creation, recording startup, VR-controller state mutation, settings writes, and license-warning dialogs remain legacy execution.src/app_core/app_frame.howns the current initial surface, update-gating, tick, resize, and draw-pass decisions consumed byApp::create,App::tick,App::resize,App::update,App::draw, andpano_cli plan-app-frame; retained layout traversal, toolbar widget writes, UI render-target recreation, canvas stroke drawing, VR UI render-target drawing, main target binding, and OpenGL/UI drawing remain in the legacy app.src/app_core/app_shutdown.howns the current shutdown cleanup staging consumed byApp::terminateandpano_cli plan-app-shutdown; retained UI-state save, recording stop, resource invalidation, layout unload, render-target/mesh destruction, and panel-node release remain in the legacy app.src/legacy_app_preference_services.*is the current app-shell bridge for options-menu preference execution. It keeps UI scale, viewport scale, RTL, VR mode, VR-controller, auto-timelapse, and canvas cursor-mode callbacks on thepp_app_coreAppPreferenceServicescontract while retained settings persistence, recording lifecycle, and legacy canvas/UI execution remain tracked byDEBT-0045; the VR mode callbacks now callAppwrappers that dispatch throughPlatformServicesbefore reaching the retained platform VR bridge.src/legacy_app_startup_services.*is the current app-shell bridge for startup preference/runtime execution. It keeps run-counter persistence, startup preference save, auto-timelapse startup, stored VR-controller state, and license-warning decisions on thepp_app_coreAppStartupServicescontract while retainedSettings,App::rec_start, and message-box execution remain tracked byDEBT-0046.src/legacy_canvas_tool_services.*is the current app-shell bridge for canvas toolbar tool selection, NodeCanvas stylus/input mode switching, and canvas hotkey/touch execution. It keeps those live paths on thepp_app_corecontracts;App::init_toolbar_draw()now consumes the testedpp_app_coredraw-toolbar binding plan exposed throughpano_cli plan-canvas-tool-toolbarbefore retained button lookup and legacy execution dispatch. Canvas mode tip visibility and pressure remapping now askPlatformServices;NodeCanvas::update_cursor()now consumespp_app_corecanvas cursor visibility planning exposed throughpano_cli plan-canvas-cursorbefore retained platform cursor dispatch. LegacyCanvasmode state, transform actions, picking, touch-lock, save/UI/cursor calls, brush-size controls, and history execution remain tracked byDEBT-0027.src/legacy_document_animation_services.*is the current UI-shell bridge for animation frame commands, timeline/selected-frame execution, playback ticks, onion-size updates, and play-mode toggles. It keeps those live paths on thepp_app_corecontracts.NodeCanvasonion-skin panorama drawing now also consumes the testedpp_app_coreonion frame range and alpha falloff helper, andNodeAnimationTimelinemouse scrubbing consumes testedpp_app_corecursor-to-frame planning exposed throughpano_cli plan-animation-timeline-scrub, andNodePanelAnimationlayer/frame/timeline display state consumes the testedpp_app_corepanel view model exposed throughpano_cli plan-animation-panel-view, while legacyCanvas/Layerframe state, canvas mode, animation-panel timeline/playback fields, and the temporaryNodePanelAnimationfriend adapter remain tracked byDEBT-0022.src/legacy_brush_ui_services.*is the current UI-shell bridge for brush color, texture, preset, stroke-refresh, brush texture-list, and stroke-control execution.NodePanelBrushPresetnow consumespp_app_corepreset-list planning andBrushPresetListServicesexecution for add/select/move/remove/ clear before the retained legacy bridge mutates child nodes.NodePanelStroke::update_controls()now consumes the testedpp_app_corestroke-panel view model for brush float settings, toggles, blend modes, and thumbnail paths, andpano_cli plan-brush-stroke-panel-viewexposes that projection for automation.App::brush_update()now consumes the testedpp_app_corebrush refresh view before updating retained stroke, quick, and floating color widgets, andpano_cli plan-brush-refreshexposes that fan-out for automation. These paths stay on thepp_app_corecontracts while legacyBrush,Canvas::I, image load/save,NodePanelBrush,NodePanelStroke, quick/color refreshes, direct preset child-node mutation, brush thumbnail/popup ownership, and the temporaryNodePanelBrushfriend adapter remain tracked byDEBT-0023.src/legacy_grid_ui_services.*is the current UI-shell bridge for grid heightmap picker/load/reload/clear, lightmap render, and heightmap commit execution. It keeps those live paths on thepp_app_corecontracts while legacy image loading, OpenGL texture updates, sharedparallel_forCPU lightmap baking/progress, andCanvas::draw_objectsexecution remain tracked byDEBT-0024.src/legacy_quick_ui_services.*is the current UI-shell bridge for quick brush/color slot selection, popup routing, mini-state restore, and mini-state reset execution. Quick size/flow slider preview cursor placement and pen/line mode tip flags now consume the testedpp_app_coreplanner directly fromNodePanelQuick, andpano_cli plan-quick-slider-previewexposes that path for automation. It keeps those live paths on thepp_app_corecontracts while legacy quick widgets, brush previews, color picker state, preset popup execution, and direct legacyCanvasMode*field writes remain tracked byDEBT-0025.pano_cli simulate-image-importdecodes an embedded tiny PNG throughpp_assets, attaches it topp_document, and is covered bypano_cli_simulate_image_import_smoke.pano_cli simulate-blendexposes deterministic final RGBA and stroke-alpha blend reference vectors through JSON automation and is covered bypano_cli_simulate_blend_smoke.pano_cli simulate-strokeexposes the pure stroke sampler for scripted automation and is covered bypano_cli_simulate_stroke_smoke.pano_cli simulate-stroke-scriptloads a text stroke script fixture and is covered bypano_cli_simulate_stroke_script_smoke.pano_cli apply-stroke-scriptparses a text stroke script fixture, samples every stroke throughpp_paint, maps the samples into a boundedpp_documentRGBA8 face payload, writes a PPI file, and is covered bypano_cli_apply_stroke_script_roundtrip_smoke, which inspects the dirty-face box and loads the generated file back as decoded document pixel data, pluspano_cli_apply_stroke_script_rejects_tiny_canvasfor invalid dimension rejection.panopainter_validate_shadersvalidates the current combined GLSL shader files for one vertex stage marker, one fragment stage marker, valid marker order, and existing relative includes.pp_renderer_apiowns the canonical PanoPainter shader catalog consumed by the legacy OpenGL app initialization path;pp_renderer_api_testsvalidates catalog size, key entries, duplicate rejection, and bad path rejection.pp_renderer_glowns headless OpenGL runtime capability detection consumed by the legacy app initialization path;pp_renderer_gl_capabilities_testsvalidates framebuffer fetch, map-buffer alignment, desktop GL float support, GLES float/half-float extensions, WebGL exclusion behavior, and the upload-type mapping used by legacyTexture2DandRTTcreation, plus the RGBA pixel-format mapping used byRTTtexture allocation. It also validates image channel-count to OpenGL texture format mapping, including invalid channel counts rejected byTexture2D::create(Image), renderer API texture-format to OpenGL internal/pixel/component token mapping including depth-stencil formats, RGBA8/RGBA32F readback formats, checked byte-count math, PBO pixel-buffer target/usage/access mapping, and PBO allocation/readback/map/unmap/delete dispatch sequences used by retainedPBOrecording readbacks, and framebuffer status naming used byRTTandTexture2Ddiagnostics. It also owns the 2D texture target, framebuffer setup, readback format, mipmap target, and update component-type tokens used byTexture2D, plus cube-map binding and allocation face targets used byTextureCube. RetainedTexture2D,TextureCube, and RTT texture allocation, bind, parameter, update, mipmap, and delete dispatch now share the retainedlegacy_gl_texture_dispatchraw callback bridge. RetainedSamplercreate, parameter, border-color, bind, and unbind dispatch now sharelegacy_gl_sampler_dispatch, and retainedPBOallocation, framebuffer readback, map, unmap, and delete dispatch now sharelegacy_gl_pixel_buffer_dispatch. It also owns and validates framebuffer blit color mask and linear/nearest filters used byRTT::resizeandRTT::copy, renderer API blit-filter to OpenGL token mapping, plus the default linear clamp-to-edge render-target texture parameters and parameter dispatch, texture/renderbuffer targets, depth format, framebuffer targets, binding queries, attachment points, render-target framebuffer allocation/delete, binding restore, and completion status used byRTT::create/RTT::destroyand framebuffer bind/restore paths, plus RTT clear color/depth masks. RetainedTexture2Dreadback and RTT framebuffer allocation, deletion, bind/restore, blit, readback, and PBO readback dispatch now share the retainedlegacy_gl_framebuffer_dispatchraw callback bridge. RTT render-target clear, masked color clear with color-write-mask restore, and texture bind/unbind now execute through tested dispatch contracts here. Optional RTT depth renderbuffer allocation/storage/delete and framebuffer depth attach/detach, plus canvas object-drawing depth renderbuffer setup, also execute through tested dispatch contracts here and share the retainedlegacy_gl_renderbuffer_dispatchraw callback bridge. Renderer API render-pass color/depth/stencil clear-mask and clear-value mapping, and color-write-mask query tokens.RTTno longer spells GL enum names directly.RTTalso exposes a retained RGBA8 region-readback helper that uses the tested framebuffer readback dispatch for canvas pick/history/snapshot and transform history paths, plus a retained RGBA8 region-update helper that uses the tested texture-update dispatch for canvas undo, layer restore, and flood-fill texture writes. 2D framebuffer-to-texture copies used by retained canvas, transform, layer-conversion, panorama UI, brush-preview, and CanvasLayer cube-face paths now execute through a testedpp_renderer_gldispatch via the target-aware retained framebuffer-copy utility bridge. That bridge remains retained until renderer services own copy execution underDEBT-0036. It also validates renderer API primitive-topology to OpenGL draw-mode mapping, Shape index-type, fill/stroke primitive-mode, buffer target, static upload usage, and vertex attribute component/normalization mapping used by the legacy mesh draw path. RetainedShape,TextMesh, andNodeColorWheelmesh buffer/VAO creation, zero-byte dynamic-buffer creation, dynamic vertex/index uploads, fill/stroke/text draw calls, and buffer/VAO deletion now consume tested dispatch contracts here throughlegacy_gl_mesh_dispatch, plus the PanoPainter cube-face to OpenGL texture-target mapping used byTextureCube. It also owns and validates sampler wrap S/T/R, min/mag filter, and desktop border-color parameter mapping used by legacySampler, plus renderer API sampler filter/address-mode to OpenGL token mapping including mirrored-repeat and aggregate renderer API sampler-state to OpenGL min/mag/wrap mapping. LegacyTextureCubeallocation/bind/delete and legacySamplercreate/configure/border/bind/unbind calls now consume those resource dispatch contracts directly from the retained app utilities. The PanoPainter shader attribute binding catalog, shader stage tokens, compile/link status queries, active-uniform count query, and matrix-uniform transpose token used by legacyShadercreation also live here. Renderer API blend factor/op to OpenGL token mapping is tested here with explicit support flags soGL_ZEROstays distinguishable from unsupported enum values. Aggregate renderer API blend-state to OpenGL enable/factor/equation/color-mask mapping, depth compare-op to OpenGL depth-function mapping, and aggregate renderer API depth-state to OpenGL enable/write/compare mapping are tested here too.Shaderno longer spells GL enum names directly. It also owns the PanoPainter shader uniform catalog and legacy hash mapping used byShaderactive-uniform discovery and the uniform uniqueness check. LegacyShaderprogram use/delete, uniform writes, and attribute-location lookup now consume tested dispatch contracts here. Legacy shader source compilation, shader deletion, program attach/link, attribute rebinding, active-uniform count/enumeration, and uniform-location discovery now consume tested dispatch contracts as well, leaving the retained shader utility with thin GL adapter functions. App OpenGL initialization debug severity, debug output, GL info string, renderer API viewport/scissor rect conversion, default depth/program-point/ line-smooth state, blend factor/equation, and UI render-target RGBA8 format tokens are cataloged and tested here too. The legacy convert command now applies its depth, program-point-size, source-alpha blend, and add-equation startup policy through a tested dispatch contract here; the resize path consumes the backend-owned mapping. App clear color-buffer masks, default framebuffer binding, scissor state, and sampler filter/wrap tokens also consume the backend mapping. OpenGL extension enumeration query tokens used before runtime capability detection are cataloged here. Legacy font atlas texture formats, text mesh buffer targets, attribute component and normalization tokens, draw primitive/index type, upload usage, and active texture unit selection also consume the backend mapping. Text mesh buffer/VAO creation, deferred index/vertex uploads, indexed draw calls, and draw-time texture-unit activation now consume testedpp_renderer_gldispatch contracts through the shared retained mesh bridge too. Canvas undo/redo dirty-region texture updates and readbacks also consume the backend-owned 2D texture target, RGBA pixel format, and unsigned-byte component mapping; canvas stroke commit, thumbnail, and object-draw history paths now consume tested capability-state query dispatch for saved blend state.NodeViewportpreview rendering also consumes backend-owned viewport query, clear-color query, color-buffer clear mask, and blend-state tokens.NodeImageTexturepreview drawing also consumes backend-owned fallback 2D texture bind and blend-state tokens.NodeImagedrawing and remote-image texture creation also consume backend-owned mipmapped sampler filters, blend-state tokens, and RGBA8/RGBA texture format mapping.NodeColorWheeltriangle-buffer setup now shares the retained mesh bridge, and its draw-state handling also consumes backend-owned blend-state tokens. Simple UI text, text-input, border, scroll, and animation timeline draw paths also consume backend-owned blend-state tokens. Canvas layer cube/equirect generation, clear, restore, and snapshot paths also consume backend-owned cube/2D texture targets, active texture units, cube texture binding, blend/clear state, viewport execution, color-buffer clear, clear-color query/restore, and RGBA8 read/write pixel mapping. Their active-texture, cube-texture binding, viewport, blend capability, clear-color, and color-buffer clear adapter endpoints now sharelegacy_ui_gl_dispatch; the cube-face framebuffer-to-texture copy now uses the shared retained target-aware utility bridge and remains tracked by DEBT-0036.NodePanelGridheightmap preview and lightmap baking also consume backend-owned texture readback formats, sampler filters, depth/blend state, depth clears, viewport queries, color-mask booleans, active texture units, and float render-target formats; its desktop texture-resize readback now reusesTexture2D::get_image()and the tested framebuffer-backed texture readback dispatch instead of directglGetTexImage; CPU lightmap row dispatch now uses sharedparallel_forinstead of platform-specific worker APIs. Its live heightmap draw and bake paths now execute depth/blend state changes, depth clears, color-write-mask toggles, active texture selection, and bake viewport changes through testedpp_renderer_gldispatch adapters. Grid depth-state snapshots and sun overlay viewport queries now use tested backend query dispatch as well. Legacyutil.cppOpenGL error naming andgl_statesave/restore also consume backend-owned error codes, state queries, framebuffer targets, texture binding targets, and active texture units.NodeStrokePreviewbrush preview rendering also consumes backend-owned depth/scissor/blend state, tested viewport/clear-color query dispatch, clear-color restore, active texture unit execution, fallback 2D texture unbinds, 2D texture targets, copy targets, and sampler filters/wraps. Its live stroke-mixer and brush-preview viewport, scissor, and depth/blend state changes now execute through testedpp_renderer_gldispatch via the sharedlegacy_ui_gl_dispatchbridge. RetainedCanvasstroke/thumbnail/object/export paths andNodeCanvaspanorama rendering use the same tested active-texture dispatch for texture-unit switches, and their live viewport, scissor, and generic depth/blend/scissor capability changes now route through the same backend dispatch contracts.NodeCanvassaved blend/depth/scissor capability-state queries now use tested backend query dispatch too. RetainedCanvasstroke draw/commit, thumbnail generation, object rendering, andLayerFrame::clearsaved viewport or clear-color query plus clear-color restore paths also use testedpp_renderer_gldispatch helpers. RetainedCanvasactive texture, fallback texture unbind, viewport/scissor execution, viewport and clear-color query, clear-color restore, and capability query/apply adapter endpoints now sharelegacy_ui_gl_dispatch; Canvas and RTT depth renderbuffer allocation/attachment/delete now sharelegacy_gl_renderbuffer_dispatchwhile renderer-resource ownership remains retained.NodeCanvassaved viewport/clear-color query, density/offscreen color clear, and clear-color restore paths use the same helpers.NodeCanvasandNodeStrokePreviewnow sharelegacy_ui_gl_dispatchfor active texture, texture unbind, viewport/scissor, clear-color, color-buffer clear, and capability query/apply adapter endpoints.CanvasModeoverlay, mask, transform, and canvas-tip pick paths now also share that bridge for active texture, capability query/apply, viewport, read-framebuffer query, and RGBA8 pixel-readback adapter endpoints.CanvasLayercube/equirect generation and frame clears also share the bridge for active texture, cube texture binding, viewport execution, blend capability execution, clear-color query/restore, and color-buffer clear adapter endpoints, while its cube-face framebuffer-to-texture copy uses the shared retained target-aware utility bridge.NodePanelGridheightmap draw and bake setup also shares it for active texture, depth/blend capability query/apply, viewport query/execution, depth clears, and color-write-mask adapter endpoints. Retained desktop HMD eye rendering also routes viewport execution through tested backend dispatch. LegacyTexture2D,TextureManager,Sampler, andRTTpublic headers no longer expose raw OpenGL enum defaults; default texture formats, sampler filters/wraps, and render-target formats resolve through backend-owned overloads. The Windows entrypoint also consumes backend-owned generic OpenGL error-code/info-string tokens, runtime string query ordering, and WGL core-context/pixel-format attribute catalogs. The headless OpenGL command planner consumespp_renderer_apirecorded commands and maps render-pass clear masks/values, viewport/scissor state, blend/depth/sampler state, texture formats, primitive modes, draw counts, and blit filters into GL-facing planned command data while rejecting unsupported enum tokens before a real GL context is needed. It also plans whole recorded command streams, preserving per-command planned data while counting render passes, draws, shader binds, shader uniforms, texture/sampler binds, texture uploads, mipmap generation, texture transitions, texture copies, texture readbacks, frame captures, passthrough commands, trace commands, unsupported commands, and render-pass ordering errors such as state changes outside a pass, nested passes, texture I/O or blits inside a pass, and unclosed passes. It also validates executable command dependencies, including shader-before-uniform and shader-plus-mesh before draw within each render pass, and rejects invalid texture/sampler bind slots in malformed recorded streams.pano_cli record-renderemits the OpenGL plan texture/sampler bind counts so automation can assert backend interpretation without an OpenGL context. Desktop VR drawing also consumes backend-owned scissor/depth/blend state, blend/depth state query-restore, depth clear masks, active texture unit dispatch, and fallback 2D texture unbind dispatch; VR SDK start/stop now dispatches throughPlatformServiceswhile retaining the existing Windows OpenVR bridge shape. Canvas mode overlay, mask, and transform paths also consume backend-owned blend/depth state execution, active texture unit dispatch, transform/cut viewport execution, 2D texture copy targets, and RGBA8 readback format tokens; canvas-tip pick readback now routes through tested framebuffer readback dispatch using the active read framebuffer, with only a local OpenGL adapter endpoint retained insrc/canvas_modes.cpp. Paint-mode blend/depth state snapshots now use tested capability-state query dispatch.NodeCanvaspanorama UI rendering also consumes backend-owned sampler defaults, tested viewport/clear-color query dispatch, blend/depth/scissor state, color-buffer clear dispatch, clear-color restore, active texture units, fallback 2D texture unbind targets, copy targets, and RGBA8 render-target formats. Canvas resource setup also consumes backend-owned stroke-buffer RGBA8/RGBA16F/RGBA32F formats, flood-fill texture upload format/type, brush/stencil/mix sampler filters and wraps, and image channel-count texture formats for cube-strip imports. Clamp-to-border sampler wrap is now part of the backend capability catalog and test coverage. Early canvas draw helpers also consume backend-owned pick readback format/type and RTT-backed region-readback execution, stroke mixer depth/scissor/blend state, saved viewport and clear-state queries, active texture units, fallback 2D texture unbind targets, and stroke background framebuffer-copy dispatch. Canvas stroke commit also consumes backend-owned saved viewport/clear/blend state, history readback format/type and RTT-backed region-readback execution, active texture units, fallback 2D texture unbind targets, and layer compositing framebuffer-copy dispatch. Canvas layer merge rendering and explicit layer-merge compositing also consume backend-owned depth/blend state, active texture units, fallback 2D texture unbind targets, and merge framebuffer copy targets. Canvas equirectangular import drawing and depth export rendering also consume backend-owned depth/blend state and active texture units. Canvas thumbnail generation and object-drawing helpers also consume backend-owned saved viewport/clear/blend state, active texture units, readback format/type, framebuffer copy targets, and depth renderbuffer allocation plus framebuffer depth attach/detach dispatch contracts;src/canvas.cppno longer contains rawGL_*constants. Windows desktop OpenGL context creation now consumes a testedwindows_wgl_core_context_3_3_config()catalog frompp_renderer_glinstead of owning active WGL context/pixel-format attribute literals inmain.cpp.windows-msvc-vcpkg-headlessvalidates manifest install/configure/build/test for the current headless component matrix; see DEBT-0007 for remaining app and platform triplet migration. Root CMake targetpanopainter_platform_build_vcpkg_ui_corefocuses that gate on the vcpkg-backedpp_ui_core/tinyxml2 boundary andpp_ui_core_layout_xml_tests.scripts/automation/analyze.*runs shader validation plus a renderer-boundary guard that reports JSON and fails if active non-backend source code reintroduces rawGL_*/WGL_*constants outside the allowed legacy OpenGL implementation files.pp_renderer_apiexposes a headlessRecordingRenderDevicethat reports renderer feature flags and validates backend-owned resource creation, explicit texture usage flags, command order, render-pass color/depth/stencil clear intent, scissor state, depth state, blend state, texture-slot binding, sampler-state binding, texture-upload byte counts, texture mip-level counts, texture/mesh/shader resource debug labels, mipmap-generation commands, texture-state transitions, shader-uniform writes, explicit draw descriptor ranges, texture-copy regions, readback/frame-capture/blit descriptor validation, readback bounds, destination buffer sizes, and render-target blit regions, records render-pass-clear/scissor/depth/blend/shader-uniform/texture-bind/ sampler-bind/draw/upload/mipmap-generation/texture-transition/texture-copy/readback/ frame-capture/blit commands, draw mesh inputs, explicit draw ranges, and records trace markers and scopes without a window or GL context. Recorderclear()also resets active render-pass and trace-scope state so automation can reuse the same recording device after an interrupted frame.pano_cli record-renderexposes the recording renderer through JSON automation, including backend feature flags, render-pass/depth-clear counts, scissor/depth/blend/ shader-uniform/texture-bind/sampler-bind/upload/mipmap-generation/texture-transition/texture-copy/readback/ frame-capture/blit command and byte totals, trace marker/scope counts, labeled descriptor counts, backend resource creation counts, plus draw descriptor vertex/index totals. Whenpp_renderer_glis available, it also emits anopenGlPlanJSON object with the planned command count, support status, render-pass/draw/shader-bind/uniform/texture-upload/mipmap/ transition/copy/readback/capture/passthrough/trace counts, unsupported command count, render-pass order error count, dependency error count, and unclosed-pass state. Its--exercise-clearmode verifies interrupted-frame recorder clear/reuse behavior and reports the result in JSON, and is covered bypano_cli_record_render_smoke,pano_cli_record_render_exercises_clear_reset, pluspano_cli_record_render_rejects_oversized_target.pano_cli simulate-document-historyexposespp_document::DocumentHistoryapply/undo/redo state through JSON automation and is covered bypano_cli_simulate_document_history_smoke.pano_cli simulate-document-editsexposespp_documentlayer metadata, frame order, active-index, tiny face-payload state, and selection-mask state through JSON automation and is covered bypano_cli_simulate_document_edits_smoke.pano_cli simulate-image-importexposes embedded PNG decode and document face-payload attachment through JSON automation and is covered bypano_cli_simulate_image_import_smoke.pano_cli import-imageexposes file-driven PNG decode and document face-payload attachment through JSON automation and is covered bypano_cli_import_image_smokeandpano_cli_import_image_rejects_truncated_png.pano_cli export-imageexposes deterministic RGBA8 PNG writing through JSON automation and is covered bypano_cli_export_image_roundtrip_smoke; full legacy canvas export remains a future CLI task.pano_cli save-projectexposes generated multi-layer, multi-frame PPI writing with layer metadata and targeted dirty-face layer/frame payloads through JSON automation and is covered by metadata-only and dirty-face-payload round-trip smoke tests; full legacy canvas save parity remains tracked by DEBT-0013.pp_document::export_ppi_project_documentexposes pure document-to-PPI byte export through CTest coverage; legacy Canvas save integration remains a future DEBT-0010/DEBT-0013 task.pano_cli simulate-document-exportexposes document export round-trip state through JSON automation for agent-driven checks.pano_cli save-document-projectexposes file-writing document export automation for inspect/load round trips.pano_cli apply-stroke-scriptexposes file-driven stroke-script application to a pure document face payload and writes a PPI artifact for inspect/load round-trip automation.pano_cli classify-openexposes thepp_app_coredocument-open route contract as JSON and is covered for project files, ABR imports, PPBR imports, and malformed path rejection.pano_cli plan-open-routeexposespp_app_coredocument-open action planning as JSON and is covered for clean project open, dirty project discard-prompt, and ABR import-prompt states.pano_cli plan-new-documentexposespp_app_corenew-document target, legacy resolution-index mapping, and overwrite-prompt planning as JSON and is covered for save-now, existing-target overwrite, and invalid-resolution states.pano_cli plan-document-fileexposespp_app_coredocument-name validation, legacy.ppipath construction, and overwrite-prompt decisions as JSON through the same combined save-file plan consumed by the live save-as dialog; it is covered for save-now and existing-target overwrite states.pano_cli plan-document-versionexposespp_app_coresave-version suffix parsing, candidate path generation, collision skipping, and no-slot failure behavior as JSON and is covered for first-version and existing-path skip states.pano_cli plan-export-targetexposespp_app_coreexport target planning for image file exports, layer/frame collection directories, picked-directory stems, and MP4 suggested names as JSON and is covered for file, collection, and suggested-name states.pano_cli plan-export-messageexposespp_app_coreexport completion dialog metadata for equirectangular image, layer/frame collection, depth/cube, animation MP4, and timelapse success paths as JSON, including platform-style destinations and suppressed/no-message paths.pano_cli plan-export-reportexposespp_app_coreexport failure and license-disabled dialog metadata as JSON; live export dialogs consume the same metadata before retained legacy export execution or logging continues.pano_cli plan-export-startexposespp_app_coreexport availability planning for license-gated, demo-blocked, and missing-canvas states as JSON; the live image, layer, animation-frame, depth, and cube-face export dialogs plus MP4 animation and timelapse export dialogs consume the same start contract before reaching legacy canvas/recording export execution.pano_cli plan-recording-sessionexposespp_app_corerecording start, stop, clear, platform cleanup, frame-count reset, and export progress-total plus export progress-dialog planning as JSON; the live recording controls consume those contracts before reaching legacy recording threads, retained PBO readback call sites, and MP4 encoder execution.src/legacy_recording_services.*is the current app-shell bridge for recording start/stop/clear and MP4 export execution. It keeps those live paths on thepp_app_corecontracts while legacy recording thread ownership, retained PBO readback call sites, progress UI lifetime, platform cleanup, andMP4Encoderexecution remain tracked byDEBT-0037.pano_cli plan-share-fileexposespp_app_coreshare availability planning as JSON for unsaved and saved document paths; the live platform share command consumes the same contract before reaching iOS/macOS sharing bridges or retained no-op platform branches.pano_cli plan-picked-pathexposespp_app_coreselected-path planning as JSON for empty and non-empty file picker results; live image/file/save/ directory picker branches consume the same contract before invoking retained platform callbacks or legacy picker bridges.pano_cli plan-display-fileexposespp_app_coreexternal file presentation planning as JSON for empty and non-empty paths; the live display-file command consumes the same contract before retained platform open-file bridges.pano_cli plan-keyboard-visibilityexposespp_app_corevirtual keyboard visibility planning as JSON for hidden and visible states; live show/hide keyboard requests consume the same contract before retained mobile platform keyboard bridges.pano_cli plan-cursor-visibilityexposespp_app_corecursor visibility planning as JSON for hidden and visible states; live canvas cursor requests consume the same contract before retained desktop platform cursor bridges.pano_cli plan-clipboard-readandpano_cli plan-clipboard-writeexposepp_app_coreclipboard text planning as JSON, including empty text writes; live clipboard get/set requests consume the same contracts before retained platform clipboard bridges.pp_platform_apiexposes the SDK-freePlatformServicesinterface for startup storage path preparation, clipboard text, cursor visibility, virtual-keyboard visibility, external file display, file sharing, native app/window close, UI-thread lifecycle hooks, render-context lifecycle hooks, render-target binding hooks, render platform hint hooks, render-capture frame hooks, render debug callback hooks, per-frame platform hooks, picker callbacks, recording cleanup, exported-image publishing, persistent storage flushing, document browse roots, working-directory picker policy and display-path formatting, canvas input tip visibility and pressure remapping, native UI/window state saving, live asset/layout reload policy, diagnostic stacktrace/crash hooks, SonarPen availability/startup, VR mode lifecycle, PPBR export data-directory policy, prepared-file writable target selection, network TLS verification policy, and prepared-file save/download handoff; PPBR and MP4 export dialogs consume the same prepared-file policy at runtime instead of spelling mobile/Web branches locally, layer/frame collection export dialogs consume the work-directory collection policy beforepp_app_coreplans immediate collection export versus directory-picker stem export, and app-owned curl upload/download/license helpers consume the TLS policy instead of spelling Android branches locally; retainedAsset::open_url,LogRemote::net_init, and cloud browse-dialog curl sites now consume the same default platform TLS policy helper inpp_platform_apiinstead of spelling Android branches locally; retained platform-family decisions for exported-image publishing, persistent-storage flushing, document browse roots, working-directory picker availability, prepared-file target planning, work-directory collection export policy, PPBR data-directory override policy, SonarPen availability, live asset reload policy, native UI/window state saving, layout XML file mtime reload policy, recording cleanup policy, default canvas resolution, and canvas tip visibility now live in the testedplatform_policycatalog and are consumed by bothWindowsPlatformServicesand the retained non-Windows fallback; Windows live app execution now uses injectedWindowsPlatformServicesfromsrc/platform_windows/windows_platform_services.*inpp_platform_windows, while non-Windows platforms still reach retained platform bridges through the debt-tracked adapter isolated insrc/platform_legacy/legacy_platform_services.*.src/app.hnow forward-declares retained platform handles instead of including Objective-C, Android, or GLFW SDK headers. The full platform SDK headers needed by retained non-Windows bridges are included insrc/platform_legacy/legacy_platform_services.cpp, keeping central app header consumers lighter whileDEBT-0055tracks removal of those handles fromApp.pp_platform_apialso ownsplan_asset_file_load, the SDK-free file-load policy consumed byLayoutManagerfor XML layout reload decisions. The pure probed decision and platform-family policy preserve desktop mtime-based reload behavior and non-desktop already-loaded skip behavior while keeping direct platform guards out of the shared layout parser; the live wrapper still owns the retainedstatprobe until platform storage/file-watch services replace it.pp_renderer_glowns the testedOpenGlInitialStatestartup depth/blend policy and dispatch application consumed byApp::init, tested runtime version/vendor/renderer/GLSL string query dispatch consumed byApp::initand Windows startup logging/title construction, tested default clear color/buffer dispatch consumed byApp::clear, tested app UI viewport/scissor dispatch consumed byApp::drawandApp::vr_draw_ui, tested generic capability query/apply and buffer-clear dispatch consumed by VR draw state setup and restore, tested saved-state snapshot/restore dispatch consumed by the retainedgl_stateutility, tested texture lifecycle/readback dispatch consumed by the retainedTexture2Dutility, tested framebuffer blit/readback dispatch consumed by retainedRTTresize/copy/readback and RGBA8 region-readback paths, tested texture-update dispatch consumed by retainedRTTRGBA8 dirty-region writes, tested render-target texture parameter, framebuffer allocation/delete, color/depth attachment, status-check, and binding-restore dispatch consumed by retainedRTT::create/RTT::destroy, tested RTT render-target clear, masked color clear with color-write-mask restore, and texture-bind dispatch, tested active-texture dispatch consumed by retained Canvas, Font, NodeCanvas, and NodeStrokePreview texture-unit switches, tested viewport/scissor/capability dispatch consumed by retained Canvas, NodeCanvas, NodeStrokePreview, and HMD render-state paths, tested capability-state query dispatch consumed by retainedCanvas,NodeCanvas,CanvasMode, andNodePanelGriddraw-state restore paths, tested viewport query, clear-color query, and clear-color restore dispatch consumed by retainedCanvas,CanvasLayer,NodeCanvas, andNodeStrokePreviewdraw-state paths, tested color-write-mask dispatch consumed by retainedNodePanelGridtransparent heightmap rendering, tested texture readback dispatch consumed byNodePanelGridtexture-resize preservation, tested pixel-buffer allocation/readback/map/unmap/delete dispatch consumed by retainedPBOrecording readbacks, tested framebuffer-to-texture copy dispatch consumed by retained canvas/UI paint paths and CanvasLayer cube-face generation, tested cube texture bind dispatch consumed by CanvasLayer equirect export, tested framebuffer bind/restore dispatch consumed by retainedRTTrender-target pass entry and exit paths, tested depth renderbuffer allocation/delete and framebuffer depth attach/detach dispatch consumed by retained RTT and canvas object-drawing helpers, shared legacy UI GL dispatch consumed by retainedNodeBorder,NodeImage,NodeImageTexture,NodeColorWheel,NodeAnimationTimeline,NodeScroll,NodeText,NodeTextInput, andNodeViewportblend-state, fallback texture unbind, viewport, color clear, and clear-color restore paths, plus retainedNodeCanvasandNodeStrokePreviewactive-texture, fallback texture unbind, viewport/scissor, clear-color, color-buffer clear, and capability query/apply draw-state adapter endpoints, plus retainedCanvasModeactive-texture, capability query/apply, viewport, read-framebuffer query, and RGBA8 pixel-readback adapter endpoints, plus retainedNodePanelGridactive-texture, depth/blend capability query/apply, viewport query/execution, depth-clear, and color-write-mask adapter endpoints, tested convert-command state dispatch consumed byApp::cmd_convert, tested render platform hint dispatch consumed byWindowsPlatformServicesand the retained macOS legacy fallback, tested debug-output state dispatch consumed byWindowsPlatformServices, plus renderer API to OpenGL token mapping and command-planning contracts used by the OpenGL parity work.pano_cli plan-cloud-uploadexposespp_app_corecloud upload availability, new-document warning, publish prompt, prompt title/message/captions, and save-before-upload planning as JSON; the live cloud upload command consumes the same start contract before reaching legacy UI, canvas save, and network upload execution.pano_cli plan-cloud-upload-allexposes bulk cloud upload file-count, progress UI availability, progress-total clamping, and progress dialog metadata as JSON; the live upload-all command consumes the same contract before reaching legacy asset file listing, OpenGL context guard, progress UI, and network upload execution.pano_cli plan-cloud-browseexposespp_app_corecloud browse availability and selected-file download planning as JSON; the live cloud browse command consumes those contracts before reaching legacy dialog, network download, canvas project-open, layer UI, and action-history execution.src/legacy_cloud_services.*is the current app-shell bridge for cloud upload, bulk upload, browse dialog, and download execution. It keeps those live paths on thepp_app_coreCloudServicescontract while the app-owned curl upload/download/license helpers now askPlatformServicesfor TLS verification policy and retained dialog/network curl sites use the shared default platform TLS helper. Cloud upload warning/publish/success copy, bulk upload progress title, and download-progress copy now come frompp_app_coredialog/progress plans before retained app-dialog bridge execution. Legacy save-before-upload, progress/message UI lifetime, network upload/download helper ownership, OpenGL context guarding,NodeDialogCloud, project open, layer refresh, and action-history reset remain tracked byDEBT-0038.pano_cli simulate-app-sessionexposespp_app_coreproject-open, app-close, save, save-as, save-version, and save-before-workflow decisions as JSON and is covered for clean, dirty, already-prompting, missing-canvas, new-document, save-as, save-version, and dirty-save-version states.- Save, New Document, and Browse dialogs now use
PlatformServicesfor working-directory picker availability and displayed absolute-path formatting, sosrc/node_dialog_open.cppandsrc/node_dialog_browse.cppno longer own desktop-only path picker branches. pp_app_core_document_route_testscovers the app document-open route contract for PPI/project files, ABR imports, PPBR imports, inner-dot names, and malformed paths before the liveApp::open_documentperforms UI or legacy canvas work.pp_app_core_document_export_testscovers export file targets, collection directory/stem targets, picked-directory stems, work-directory versus picker-stem collection target planning, MP4 suggested names, and invalid export naming inputs, plus export-start license/canvas availability decisions, export menu executor dispatch, file/stem/collection export execution dispatch, failed directory creation preservation, named depth/cube export dispatch, malformed export target rejection, video export dispatch for animation MP4/timelapse paths, and empty video-path rejection.pp_app_core_document_recording_testscovers recording start/stop, clear, platform recorded-file cleanup, frame-count reset, export progress totals, oversized progress-total clamping, and recording-worker encode-wake eligibility when recording, encoder, and canvas-document state vary.pp_app_core_document_sharing_testscovers saved-path gating before platform share execution.pp_app_core_document_platform_io_testscovers empty selected-path filtering and non-empty picked-path callback planning, plus empty/non-empty display-file planning before platform picker/display callbacks, plus virtual keyboard show/hide planning before platform keyboard callbacks, plus cursor visibility planning before platform cursor callbacks, plus clipboard read/write planning before platform clipboard callbacks.pp_app_core_app_preferences_testscovers UI scale/font-scale planning, scale-option selection, viewport scale planning, RTL direction planning, timelapse start/stop/no-op decisions, VR mode success/failure dispatch, simple stored preferences, andAppPreferenceServicesexecution dispatch for options-menu side effects.pp_app_core_app_dialog_testscovers app-level progress/message/input dialog metadata planning, progress initialization, negative progress-total clamping, message cancel-button/caption policy, input OK-caption propagation, and malformed empty OK-caption rejection without requiring legacyNode*dialogs.src/legacy_app_dialog_services.*is the current app-shell bridge betweenpp_app_coreapp dialog plans and retainedNodeProgressBar,NodeMessageBox, andNodeInputBoxcreation.App::show_progress,App::message_box, andApp::input_boxnow act as thin adapters while layout insertion, callback wiring, and dialog lifetime remain tracked byDEBT-0058.pp_app_core_app_startup_testscovers startup run-counter increment planning, optional auto-timelapse/license/VR-controller decisions, negative and overflow run-counter rejection, stable full startup dispatch ordering, split persistence/runtime dispatch, and malformed startup-plan rejection.pp_app_core_app_frame_testscovers the legacy initial surface default, idle/redraw/animation update gating, canvas-stroke draw eligibility, VR UI visibility, main UI suppression in VR-only mode, tick layout selection, resize render-target/redraw projection, invalid resize rejection, redraw reset planning, UI observer clipping, on-screen enter/leave transition decisions, scissor projection, and malformed observer geometry rejection.pp_app_core_app_thread_testscovers render/UI task dispatch, immediate same-thread execution, unique queued-task replacement, stopped-worker no-wait behavior, render queue context wrapping, UI tick redraw scheduling, UI-loop frame/FPS/reload timer thresholds, malformed timer rejection, redraw frame-count projection, and thread start/stop intents.pp_app_core_app_input_testscovers pointer coordinate normalization, invalid pointer/gesture inputs, designer-first mouse routing, mouse-cancel routing, gesture midpoint/distance/delta math, main-layout routing, key state mutation intent, VR spacebar camera-sync intent, UI visibility toggle target selection, malformed UI-toggle layout rejection, and stylus touch-lock intent.pp_app_core_app_shutdown_testscovers legacy shutdown cleanup staging for UI-state save, stroke-preview renderer shutdown, recording stop, texture/shader invalidation, layout unload, render-target/mesh destruction, panel-node release, and quick-mode cleanup.pp_app_core_command_convert_testscovers command-line panorama conversion sequencing for renderer-state setup, temporary canvas allocation, project open, equirectangular export, malformed input rejection, malformed-plan rejection, and exact executor dispatch order.pp_platform_api_testscovers service dispatch for clipboard read/write, empty clipboard writes, cursor visibility, virtual-keyboard visibility, external file display, file sharing, VR lifecycle, layout/asset file load policy including pure probed reload behavior, platform-family export/storage/browse/prepared-file/UI-state/canvas policies, and picker callbacks without platform SDK headers or a window.pp_app_core_document_cloud_testscovers cloud upload no-canvas, new-document warning, clean publish prompt, and dirty save-before-upload decisions, plus cloud browse no-canvas/show-browser and selected-download decisions, plus bulk upload progress visibility, zero-file, and clamped progress-total decisions, plus cloud download/upload transfer request validation, progress-callback enablement, TLS-verification policy, and zero/negative/overrun transfer-progress fraction guards.pp_app_core_document_session_testscovers clean and dirty app session, document-open action planning and executor dispatch/rejection, save-request, close-request executor dispatch/no-op preservation, document-save executor dispatch/no-op preservation, document-session prompt metadata for close, save-before-workflow, overwrite, and save-error dialogs, save-before-workflow executor dispatch, new-document target/resolution/overwrite planning and executor dispatch, document file target, combined save-file overwrite planning and executor dispatch, plus save-version target decisions and executor validation without requiring a window, canvas, or message box.src/legacy_document_open_services.*is the current app-shell bridge betweenpp_app_coredocument-open plans and live ABR/PPBR import prompts, unsaved-project discard prompts, project opening, layer UI refresh, title updates, and action-history clearing. Accepted brush import prompts now delegate import execution tosrc/legacy_brush_package_import_services.*; remaining legacy document-open ownership is tracked byDEBT-0039.src/legacy_brush_package_import_services.*is the current app-shell bridge betweenpp_app_coreABR/PPBR brush package import requests and liveNodePanelBrushPreset::import_abr/import_ppbrexecution. It preserves the detached legacy import worker threads and preset-panel storage ownership while brush asset/paint/UI ownership is tracked byDEBT-0048.pp_app_core_brush_package_import_testscovers ABR and PPBR import kind naming, path validation, service dispatch, and empty-path rejection without requiring a window, brush preset panel, or filesystem read.src/legacy_document_session_services.*is the current app-shell bridge betweenpp_app_coredocument-session decisions and live close prompts, save dialogs, save-version routing, existing-project saves, and save-before-workflow prompts. Close, save-before-workflow, new-document overwrite, Save As overwrite, and save-error prompt text/captions now come from the pure document-session prompt catalog exposed throughpano_cli plan-document-session-prompt; retained prompt boxes are created throughsrc/legacy_app_dialog_services.*before the document-session bridge wires callbacks. It also bridges accepted new-document plans to legacy canvas resize/layer setup, overwrite prompts, title updates, and keyboard/dialog cleanup. Accepted Save As and Save Version plans now also route through this bridge before reaching legacy project-save execution, overwrite prompts, document field updates, title updates, and keyboard/dialog cleanup. Retained legacy UI/canvas execution remains tracked byDEBT-0040,DEBT-0041, andDEBT-0042.src/legacy_document_export_services.*is the current app-shell bridge betweenpp_app_coredocument export execution plans and live equirectangular, layers, animation-frame, depth, and cube-face export calls. It preserves platform-specific export messages, directory creation, picker-selected stem exports, Web prepared-file handoff, and legacyCanvasexport execution while retained renderer/document/platform ownership is tracked byDEBT-0043. It also bridges timelapse and animation MP4 export picker-selected paths while preserving desktop worker-thread timelapse behavior, mobile/Web save callbacks,App::rec_export, animationCanvas::export_anim_mp4, and success messages; retained video/export ownership is tracked byDEBT-0044.src/legacy_brush_package_export_services.*is the current app-shell bridge betweenpp_app_corePPBR brush package export requests and liveNodePanelBrushPreset::export_ppbrexecution. It preserves dialog metadata, the retained legacyImageheader object, desktop worker-thread export, mobile/Web save completion, and dialog lifetime while the PPBR preview data-directory override and export success-dialog metadata now come fromPlatformServices/pp_app_core; remaining brush asset/storage/UI/platform ownership is tracked byDEBT-0047.src/assets/brush_package.*owns the first headless PPBR package helpers: header validation, legacy-compatible version acceptance, export path normalization, preview-data-directory planning, and imported brush tip/pattern image target paths. Live export maps the active platform PPBR data-directory policy into these helpers, and liveNodePanelBrushPreset::export_ppbr/import_ppbrand ABR import image writes consume these helpers, but legacy Serializer/Image payload parsing, preview rendering, preset storage, duplicate policy, and strict-version cleanup remain tracked byDEBT-0047,DEBT-0048, andDEBT-0049.pp_assets_brush_package_testscovers PPBR header parsing, truncated/bad magic rejection, legacy version tolerance, export package/data path planning, legacy extension containment, paths the legacy regex could not match, brush tip/pattern image target planning, and invalid imported image target inputs.pp_app_core_brush_package_export_testscovers PPBR export request path validation, metadata preservation, legacy-flexible destination/export-data combinations, service dispatch, malformed request rejection, and legacy success-dialog metadata without requiring a window, brush preset panel, or filesystem write.pano_cli plan-brush-package-exportemits the same success-dialog plan for automation.src/legacy_history_services.*is the current app-shell bridge betweenpp_app_corehistory plans and legacyActionManager; toolbar andNodeCanvashotkeys share it while document-history extraction remains tracked by DEBT-0026.src/legacy_document_canvas_services.*is the current app-shell bridge betweenpp_app_corecanvas-clear/resize plans and legacyCanvas; toolbar clear, Layer menu clear, and the resize dialog share it while document/canvas execution extraction remains tracked by DEBT-0020 and DEBT-0028.pp_ui_coreconsumes vcpkg tinyxml2 only whenPP_USE_VCPKG_TINYXML2=ONthrough the vcpkg preset; default and Android validation still use the retained vendored fallback tracked by DEBT-0012.
Known warnings after the current CMake app build:
- Legacy code/vendor warnings under
/W4. pp_legacy_vendorintentionally owns retained third-party source builds for now, including JPEG, SQLite, Yoga, poly2tri, GLAD, fmt, Wacom utilities, and other patched/embedded sources. Each dependency should either move to vcpkg, an SDK import target, or a documented permanent vendored target.pp_legacy_assets_iois an object-library containment boundary for retained ABR, asset/file, binary stream, image, serializer, and settings code. It should shrink as app I/O consumespp_assetsdirectly.src/asset.his now Android-SDK-free and exposesAsset::set_android_asset_managerwith opaque handles instead of public mutable Android asset-manager state or SDK forward declarations; concrete Android asset-manager headers remain inasset.cppand the retained Android entrypoint while DEBT-0056 tracks replacing the static Android asset bridge with injected asset storage. The retained Android standard package now linksnative-libfor arm64 through the refreshed C++23 package CMake path; DEBT-0060 tracks the generatednanortoverlay that keeps that package gate clean without modifying the vendor submodule.pp_legacy_paint_documentis an object-library containment boundary for retained action, bezier, brush, canvas, canvas-layer, and event code. It should shrink as app painting and document behavior consumepp_paintandpp_documentdirectly. Sharedcanvas.hno longer owns the platformCANVAS_RESmacro; default canvas allocation now asksPlatformServices, preserving the WebGL 512 default through the testedpp_platform_apiplatform policy while DEBT-0057 tracks moving that policy into an injected Web platform service.pp_legacy_engineintentionally contains retained legacy runtime shell sources for now, so it concentrates existing legacy tablet, video, HMD, log, and low-level utility warnings until those paths move to cleaner component ownership.pp_legacy_renderer_glis an object-library containment boundary because the retained OpenGL runtime classes still include legacy app/engine headers and are still consumed directly by canvas and UI classes. It should become a normal backend library once those call sites depend onpp_renderer_api.pp_renderer_glnow owns OpenGL runtime build-target classification through CMake target compile definitions andopengl_runtime_for_current_build(), so app shader startup asks the backend for desktop GL/GLES/WebGL policy instead of carrying local platform branches. It also owns headless-tested OpenGL extension enumeration throughquery_opengl_extensions, moving the extension count/string query loop out ofapp_shaders.cpp. Startup feature negotiation now usesquery_opengl_capability_detection, so extension enumeration, runtime capability policy, and renderer-neutral feature conversion are validated together before the retainedShaderManagerstatic flags are updated.RenderDeviceFeaturesnow includes float32-linear filtering, and live canvas, diagnostics, and grid lightmap/bake decisions consume that renderer-neutral snapshot rather than reading retainedShaderManager::ext_*fields directly.pp_legacy_ui_coreis an object-library containment boundary because the retained baseNodecontrols still depend on legacy renderer and app headers. It should shrink as layout parsing, colors, generic controls, and text/image primitives move topp_ui_core.pp_panopainter_uicurrently surfaces existing legacyNode/Serializerheader and static-analysis warnings while it still depends onpp_legacy_app; these should be reduced as the UI core/app UI boundary is tightened instead of suppressed globally.pp_app_coreis the first pure app-engine target consumed bypanopainter_app; it should grow only with UI-free command routing, validation, and app service contracts that can be tested without a window.panopainter_appcurrently surfaces existing app orchestration, GLM, base64, VR, and serializer warnings now that app sources live in the composition target; warning cleanup should follow component ownership rather than be hidden with target-wide suppressions.- Visual Studio vcpkg manifest warning because manifest mode is not enabled.
LNK4099missingyuv.pdbfor retained libyuv binaries.LNK4098runtime library conflict from retained vendor binaries.
Platform-specific commands should be added here when verified locally.