# Build And Platform Inventory

Status: live
Last updated: 2026-06-03

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` | Native library target `native-lib` |
| Android Quest | `android/quest/build.gradle`, `android/quest/CMakeLists.txt` | OVR SDK imported libraries |
| Android Focus/Wave | `android/focus/build.gradle`, `android/focus/CMakeLists.txt` | Wave SDK imported libraries |
| Linux | `linux/CMakeLists.txt` | Old CMake 3.4, C++14 flag |
| WebGL/Emscripten | `webgl/CMakeLists.txt` | Old CMake 3.4, WebGL2 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.

```powershell
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 --preset android-arm64
powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64
powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug
cmake --fresh --preset windows-clangcl-asan
```

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 version` reports 2025-11-19)
- Android SDK: `C:\Users\omara\AppData\Local\Android\Sdk`
- Android NDK: `C:\Users\omara\AppData\Local\Android\Sdk\ndk\29.0.14206865`
- clang-cl: `C:\Program Files\LLVM\bin\clang-cl.exe` reports 18.1.8, but the
  selected VS 2026-preview STL expects Clang 20 or newer; see DEBT-0014 before
  treating `windows-clangcl-asan` as a passing sanitizer gate.
- Android arm64 headless configure/build passes through root CMake and the
  `platform-build` automation wrapper for `pp_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, PNG metadata and
  decode, PPI header/layout/non-finite opacity and blend-mode rejection, settings document, document
  snapshot/per-layer-frame/move/duration/face-pixel/PPI export coverage,
  snapshot-embedded duplicate/invalid face-payload and selection-mask rejection, paint brush/final-blend/
  stroke-alpha-blend/stroke spacing/stroke stress/stroke-script coverage,
  renderer shader descriptor and OpenGL capability coverage, UI
  color parsing, and layout XML parse coverage.
- Root CMake exposes named `fuzz` and `stress` CTest presets. `fuzz` currently
  runs deterministic parser/serializer edge tests for binary streams, image
  metadata, PPI, stroke scripts, and layout XML; `stress` currently runs the
  stroke sampler stress coverage.
- `pano_cli inspect-image` reports PNG IHDR metadata as JSON and is covered by
  `pano_cli_inspect_png_metadata_smoke` with a tiny IHDR fixture.
- `pp_assets_image_pixels_tests` decodes 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-image` accepts a PNG path, decodes RGBA8 pixels through
  `pp_assets`, attaches them to a pure `pp_document` face payload, and is
  covered for checked-in decodable PNG import by `pano_cli_import_image_smoke`
  and metadata-valid truncated PNG rejection by
  `pano_cli_import_image_rejects_truncated_png`.
- `pano_cli export-image` writes a deterministic RGBA8 PNG through `pp_assets`
  and is covered by `pano_cli_export_image_roundtrip_smoke`, which imports the
  generated file back through `pano_cli import-image`.
- `pano_cli inspect-project` reports validated PPI thumbnail/body byte layout,
  body summary fields, layer/frame descriptors, and dirty-face PNG payload
  metadata, and is covered by `pano_cli_inspect_project_layout_smoke` with a
  minimal PPI fixture.
- `pp_document_ppi_import_tests` attaches decoded PPI dirty-face payloads to
  `pp_document` layer/frame storage and rejects payloads outside document
  layers.
- `pp_document_ppi_export_tests` exports pure `pp_document` metadata,
  per-layer frame durations, and RGBA8 face payloads to PPI bytes through
  `pp_assets`, then decodes and reimports them for round-trip coverage.
- `pano_cli simulate-document-export` exposes the same pure document-to-PPI
  export, asset-level decode, and document reimport path through JSON
  automation and is covered by `pano_cli_simulate_document_export_smoke`.
- `pano_cli save-document-project` writes that pure document export to a PPI
  file and is covered by `pano_cli_save_document_project_roundtrip_smoke`,
  which inspects and loads the generated file.
- `pano_cli load-project` creates a `pp_document` projection with per-layer
  frame counts, durations, and decoded face-pixel payloads when present; the
  metadata-only minimal fixture remains covered by
  `pano_cli_load_project_metadata_smoke`.
- `pp_assets::create_ppi_project` writes 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-project` exposes that path for automation and is
  covered by `pano_cli_save_project_roundtrip_smoke` and
  `pano_cli_save_project_payload_roundtrip_smoke`, which reload generated
  metadata-only and targeted dirty-face-payload projects through
  `pano_cli load-project`, plus
  `pano_cli_save_project_rejects_non_finite_opacity`, which verifies rejected
  automation floats do not create output files.
- `pano_cli create-document` supports `--frames` and `--frame-duration-ms` and
  is covered by `pano_cli_create_animation_document_smoke`.
- `pano_cli simulate-document-edits` exercises pure document layer/frame edit
  operations, renderer-free face payloads, and renderer-free selection masks,
  and is covered by `pano_cli_simulate_document_edits_smoke`.
- `pano_cli simulate-document-history` exercises pure document history
  apply/undo/redo behavior and is covered by
  `pano_cli_simulate_document_history_smoke`.
- `pano_cli simulate-image-import` decodes an embedded tiny PNG through
  `pp_assets`, attaches it to `pp_document`, and is covered by
  `pano_cli_simulate_image_import_smoke`.
- `pano_cli simulate-blend` exposes deterministic final RGBA and stroke-alpha
  blend reference vectors through JSON automation and is covered by
  `pano_cli_simulate_blend_smoke`.
- `pano_cli simulate-stroke` exposes the pure stroke sampler for scripted
  automation and is covered by `pano_cli_simulate_stroke_smoke`.
- `pano_cli simulate-stroke-script` loads a text stroke script fixture and is
  covered by `pano_cli_simulate_stroke_script_smoke`.
- `pano_cli apply-stroke-script` parses a text stroke script fixture, samples
  every stroke through `pp_paint`, maps the samples into a bounded
  `pp_document` RGBA8 face payload, writes a PPI file, and is covered by
  `pano_cli_apply_stroke_script_roundtrip_smoke`, which inspects the dirty-face
  box and loads the generated file back as decoded document pixel data, plus
  `pano_cli_apply_stroke_script_rejects_tiny_canvas` for invalid dimension
  rejection.
- `panopainter_validate_shaders` validates the current combined GLSL shader
  files for one vertex stage marker, one fragment stage marker, valid marker
  order, and existing relative includes.
- `pp_renderer_api` owns the canonical PanoPainter shader catalog consumed by
  the legacy OpenGL app initialization path; `pp_renderer_api_tests` validates
  catalog size, key entries, duplicate rejection, and bad path rejection.
- `pp_renderer_gl` owns headless OpenGL runtime capability detection consumed
  by the legacy app initialization path; `pp_renderer_gl_capabilities_tests`
  validates framebuffer fetch, map-buffer alignment, desktop GL float support,
  GLES float/half-float extensions, WebGL exclusion behavior, and the
  upload-type mapping used by legacy `Texture2D` and `RTT` creation, plus the
  RGBA pixel-format mapping used by `RTT` texture allocation. It also validates
  image channel-count to OpenGL texture format mapping, including
  invalid channel counts rejected by `Texture2D::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, and PBO pixel-buffer target/usage/access
  mapping used by `RTT` and `PBO` readbacks, and framebuffer status naming
  used by `RTT` and `Texture2D` diagnostics. It also owns the 2D texture target,
  framebuffer setup, readback format, mipmap target, and update component-type
  tokens used by `Texture2D`, plus cube-map binding and allocation face targets
  used by `TextureCube`. It also owns and
  validates framebuffer blit color mask and linear/nearest filters used by
  `RTT::resize` and `RTT::copy`, renderer API blit-filter to OpenGL token
  mapping, plus the default linear clamp-to-edge
  render-target texture parameters, texture/renderbuffer targets, depth format,
  framebuffer targets, binding queries, attachment points, and completion
  status used by `RTT::create` and framebuffer bind/restore paths, plus RTT
  clear color/depth masks, renderer API render-pass color/depth/stencil
  clear-mask and clear-value mapping, and color-write-mask query tokens. `RTT` no longer
  spells GL enum names directly. 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. Legacy `Shape` mesh buffer/VAO creation, zero-byte
  dynamic-buffer creation, dynamic vertex/index uploads, fill/stroke draw
  calls, and buffer/VAO deletion now consume tested dispatch contracts here,
  plus the PanoPainter cube-face to OpenGL
  texture-target mapping used by `TextureCube`.
  It also owns and validates sampler wrap S/T/R, min/mag filter, and desktop
  border-color parameter mapping used by legacy `Sampler`, 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.
  Legacy `TextureCube` allocation/bind/delete and legacy `Sampler`
  create/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 legacy `Shader` creation also live here. Renderer API blend factor/op to
  OpenGL token mapping is tested here with explicit support flags so `GL_ZERO`
  stays 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.
  `Shader` no longer spells GL enum
  names directly. It also owns the PanoPainter shader uniform catalog and legacy hash
  mapping used by `Shader` active-uniform discovery and the uniform uniqueness
  check. Legacy `Shader` program 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, including the legacy convert command
  and resize path. 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, and indexed draw calls
  now consume tested `pp_renderer_gl` mesh dispatch contracts 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.
  `NodeViewport` preview rendering also consumes backend-owned viewport query,
  clear-color query, color-buffer clear mask, and blend-state tokens.
  `NodeImageTexture` preview drawing also consumes backend-owned fallback 2D
  texture bind and blend-state tokens.
  `NodeImage` drawing and remote-image texture creation also consume
  backend-owned mipmapped sampler filters, blend-state tokens, and RGBA8/RGBA
  texture format mapping.
  `NodeColorWheel` triangle-buffer setup and draw-state handling also consume
  backend-owned array-buffer, static-upload, vertex-attribute, primitive-mode,
  and 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,
  blend/clear state, and RGBA8 read/write pixel mapping.
  `NodePanelGrid` heightmap 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.
  Legacy `util.cpp` OpenGL error naming and `gl_state` save/restore also
  consume backend-owned error codes, state queries, framebuffer targets,
  texture binding targets, and active texture units.
  `NodeStrokePreview` brush preview rendering also consumes backend-owned
  depth/scissor/blend state, viewport/clear-color queries, active texture
  units, 2D texture targets, copy targets, and sampler filters/wraps.
  Legacy `Texture2D`, `TextureManager`, `Sampler`, and `RTT` public 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 and WGL core-context/pixel-format attribute
  catalogs.
  The headless OpenGL command planner consumes `pp_renderer_api` recorded
  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-render` emits 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,
  depth clear masks, active texture units, and fallback 2D texture unbind
  targets while retaining the existing VR SDK/platform bridge shape.
  Canvas mode overlay, mask, and transform paths also consume backend-owned
  blend/depth state, active texture units, 2D texture copy targets, and RGBA8
  readback format tokens.
  `NodeCanvas` panorama UI rendering also consumes backend-owned sampler
  defaults, viewport/clear-state queries, blend/depth/scissor state, color
  clear masks, 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, stroke mixer depth/scissor/blend state, saved viewport and
  clear-state queries, active texture units, fallback 2D texture unbind
  targets, and stroke background copy targets.
  Canvas stroke commit also consumes backend-owned saved viewport/clear/blend
  state, history readback format/type, active texture units, fallback 2D
  texture unbind targets, and layer compositing copy targets.
  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 renderbuffer/depth
  attachment parameters; `src/canvas.cpp` no longer contains raw `GL_*`
  constants.
  Windows desktop OpenGL context creation now consumes a tested
  `windows_wgl_core_context_3_3_config()` catalog from `pp_renderer_gl` instead
  of owning active WGL context/pixel-format attribute literals in `main.cpp`.
- `windows-msvc-vcpkg-headless` validates manifest install/configure/build/test
  for the current headless component matrix; see DEBT-0007 for remaining app
  and platform triplet migration.
- `scripts/automation/analyze.*` runs shader validation plus a
  renderer-boundary guard that reports JSON and fails if active non-backend
  source code reintroduces raw `GL_*`/`WGL_*` constants outside the allowed
  legacy OpenGL implementation files.
- `pp_renderer_api` exposes a headless `RecordingRenderDevice` that 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. Recorder
  `clear()` also resets active render-pass and trace-scope state so automation
  can reuse the same recording device after an interrupted frame.
- `pano_cli record-render` exposes 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. When `pp_renderer_gl` is available, it also
  emits an `openGlPlan` JSON 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-clear` mode verifies
  interrupted-frame recorder clear/reuse behavior and reports the result in
  JSON, and is covered by `pano_cli_record_render_smoke`,
  `pano_cli_record_render_exercises_clear_reset`, plus
  `pano_cli_record_render_rejects_oversized_target`.
- `pano_cli simulate-document-history` exposes `pp_document::DocumentHistory`
  apply/undo/redo state through JSON automation and is covered by
  `pano_cli_simulate_document_history_smoke`.
- `pano_cli simulate-document-edits` exposes `pp_document` layer metadata,
  frame order, active-index, tiny face-payload state, and selection-mask state
  through JSON automation and is covered by
  `pano_cli_simulate_document_edits_smoke`.
- `pano_cli simulate-image-import` exposes embedded PNG decode and document
  face-payload attachment through JSON automation and is covered by
  `pano_cli_simulate_image_import_smoke`.
- `pano_cli import-image` exposes file-driven PNG decode and document
  face-payload attachment through JSON automation and is covered by
  `pano_cli_import_image_smoke` and
  `pano_cli_import_image_rejects_truncated_png`.
- `pano_cli export-image` exposes deterministic RGBA8 PNG writing through JSON
  automation and is covered by `pano_cli_export_image_roundtrip_smoke`; full
  legacy canvas export remains a future CLI task.
- `pano_cli save-project` exposes 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_document` exposes 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-export` exposes document export round-trip state
  through JSON automation for agent-driven checks.
- `pano_cli save-document-project` exposes file-writing document export
  automation for inspect/load round trips.
- `pano_cli apply-stroke-script` exposes 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-open` exposes the `pp_app_core` document-open route
  contract as JSON and is covered for project files, ABR imports, PPBR
  imports, and malformed path rejection.
- `pano_cli plan-open-route` exposes `pp_app_core` document-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-document` exposes `pp_app_core` new-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-file` exposes `pp_app_core` document-name
  validation, legacy `.ppi` path 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-version` exposes `pp_app_core` save-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-target` exposes `pp_app_core` export 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-start` exposes `pp_app_core` export 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-session` exposes `pp_app_core` recording start,
  stop, clear, platform cleanup, frame-count reset, and export progress-total
  planning as JSON; the live recording controls consume those contracts before
  reaching legacy recording threads, PBO readback, and MP4 encoder execution.
- `pano_cli plan-share-file` exposes `pp_app_core` share 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-path` exposes `pp_app_core` selected-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-file` exposes `pp_app_core` external 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-visibility` exposes `pp_app_core` virtual 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-visibility` exposes `pp_app_core` cursor 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-read` and `pano_cli plan-clipboard-write` expose
  `pp_app_core` clipboard text planning as JSON, including empty text writes;
  live clipboard get/set requests consume the same contracts before retained
  platform clipboard bridges.
- `pp_platform_api` exposes the SDK-free `PlatformServices` interface 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, and recording cleanup, live asset/layout reload policy, diagnostic
  stacktrace/crash hooks, prepared-file save/download handoff;
  Windows
  live app execution now uses injected
  `WindowsPlatformServices` from
  `src/platform_windows/windows_platform_services.*` in `pp_platform_windows`,
  while non-Windows platforms still reach retained platform bridges through
  the debt-tracked adapter isolated in
  `src/platform_legacy/legacy_platform_services.*`.
- `pp_renderer_gl` owns the tested `OpenGlInitialState` startup depth/blend
  policy and dispatch application consumed by `App::init`, tested runtime
  version/vendor/renderer/GLSL string query dispatch, tested default clear
  color/buffer dispatch consumed by `App::clear`, tested app UI
  viewport/scissor dispatch consumed by `App::draw` and `App::vr_draw_ui`,
  tested generic capability/buffer-clear dispatch consumed by VR draw state
  setup, tested saved-state snapshot/restore dispatch consumed by the retained
  `gl_state` utility, tested texture lifecycle/readback dispatch consumed by
  the retained `Texture2D` utility, tested framebuffer blit/readback dispatch
  consumed by retained `RTT` resize/copy/readback paths, tested framebuffer
  bind/restore dispatch consumed by retained `RTT` render-target pass entry
  and exit paths, plus renderer API to OpenGL token mapping and command-planning
  contracts used by the OpenGL parity work.
- `pano_cli plan-cloud-upload` exposes `pp_app_core` cloud upload availability,
  new-document warning, publish prompt, 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-all` exposes bulk cloud upload file-count,
  progress UI availability, and progress-total clamping 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-browse` exposes `pp_app_core` cloud 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.
- `pano_cli simulate-app-session` exposes `pp_app_core` project-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.
- `pp_app_core_document_route_tests` covers the app document-open route
  contract for PPI/project files, ABR imports, PPBR imports, inner-dot names,
  and malformed paths before the live `App::open_document` performs UI or
  legacy canvas work.
- `pp_app_core_document_export_tests` covers export file targets, collection
  directory/stem targets, picked-directory stems, MP4 suggested names, and
  invalid export naming inputs, plus export-start license/canvas availability
  decisions.
- `pp_app_core_document_recording_tests` covers recording start/stop, clear,
  platform recorded-file cleanup, frame-count reset, export progress totals,
  and oversized progress-total clamping.
- `pp_app_core_document_sharing_tests` covers saved-path gating before platform
  share execution.
- `pp_app_core_document_platform_io_tests` covers 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_platform_api_tests` covers service dispatch for clipboard read/write,
  empty clipboard writes, cursor visibility, virtual-keyboard visibility,
  external file display, file sharing, and picker callbacks without platform
  SDK headers or a window.
- `pp_app_core_document_cloud_tests` covers 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.
- `pp_app_core_document_session_tests` covers clean and dirty app session,
  document-open action planning, save-request, save-before-workflow,
  new-document target/resolution/overwrite planning, document file target,
  combined save-file overwrite planning, and save-version target decisions
  without requiring a window, canvas, or message box.
- `pp_ui_core` consumes vcpkg tinyxml2 only when `PP_USE_VCPKG_TINYXML2=ON`
  through 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_vendor` intentionally 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_io` is an object-library containment boundary for retained
  ABR, asset/file, binary stream, image, serializer, and settings code. It
  should shrink as app I/O consumes `pp_assets` directly.
- `pp_legacy_paint_document` is 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 consume `pp_paint` and
  `pp_document` directly.
- `pp_legacy_engine` intentionally 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_gl` is 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 on `pp_renderer_api`.
- `pp_legacy_ui_core` is an object-library containment boundary because the
  retained base `Node` controls still depend on legacy renderer and app
  headers. It should shrink as layout parsing, colors, generic controls, and
  text/image primitives move to `pp_ui_core`.
- `pp_panopainter_ui` currently surfaces existing legacy `Node`/`Serializer`
  header and static-analysis warnings while it still depends on
  `pp_legacy_app`; these should be reduced as the UI core/app UI boundary is
  tightened instead of suppressed globally.
- `pp_app_core` is the first pure app-engine target consumed by
  `panopainter_app`; it should grow only with UI-free command routing,
  validation, and app service contracts that can be tested without a window.
- `panopainter_app` currently 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.
- `LNK4099` missing `yuv.pdb` for retained libyuv binaries.
- `LNK4098` runtime library conflict from retained vendor binaries.

Platform-specific commands should be added here when verified locally.
