Files
panopainter/docs/modernization/tasks.md

1153 lines
55 KiB
Markdown

# Modernization Task Tracker
Status: live
Last updated: 2026-06-13
This file turns the modernization roadmap into small, measurable work items.
The roadmap explains direction, the debt log explains why shortcuts remain, and
this tracker is the execution queue. Prefer closing one task here over adding a
new broad roadmap paragraph.
## Operating Rules
- Pick one `Ready` task at a time unless the user asks for planning only.
- Keep each slice small enough to validate and commit in one session.
- Do not claim percentage progress for "narrowed" debt. Points move only when a
task row is changed to `Done`.
- A task is `Done` only when its listed checks pass, the debt log is updated or
closed as applicable, and the roadmap/task score is updated.
- If a task proves too large, split it before editing code. The original task
stays `Ready` or becomes `Blocked` with the reason.
- After a verified task is committed and pushed, reset conversation context
before starting the next task when practical.
- When the user asks for subagents or delegation, follow
`docs/modernization/director-workflow.md` and keep each delegated task mapped
to a row in this tracker.
## Progress Scorecard
The current score is intentionally conservative. It should move in visible,
auditable steps rather than by subjective estimates.
| Area | Weight | Current | Progress Rule |
| --- | ---: | ---: | --- |
| Build and CMake ownership | 15 | 13 | Root CMake owns active source lists, app/tool targets, and retained package entrypoints. |
| Test and automation coverage | 15 | 9 | Headless, platform, package, and focused validation commands exist and are current. |
| Pure component behavior ownership | 15 | 8 | Behavior lives in `pp_*` components and is consumed by live adapters. |
| Legacy adapter retirement | 20 | 7 | `legacy_*_services` and singleton bridges are deleted or reduced to trivial composition. |
| Renderer boundary and OpenGL parity | 15 | 11 | Live render/export/readback paths execute through renderer interfaces with parity checks. |
| Platform and package parity | 10 | 6 | Required platforms have root CMake/package validation and injected platform services. |
| Hardening and future backend readiness | 10 | 2 | Edge, fuzz, golden, stress, and backend-lab gates exist for high-risk paths. |
| **Total** | **100** | **55** | Only completed tasks below may change this number. |
When updating `Current`, add a dated note under "Completed Task Log" with the
task id, points moved, validation command, and commit hash.
## Task States
| State | Meaning |
| --- | --- |
| `Ready` | Clear enough for an agent to execute. |
| `In progress` | Actively being changed in the current slice. |
| `Blocked` | Needs a user decision, missing toolchain, or a prior task. |
| `Done` | Validated, documented, committed, and pushed. |
## Ready Queue
### MT-001 - Adopt Measurable Task Tracking
Status: Done
Score: no score movement
Debt: none
Scope: `docs/modernization/tasks.md`, `docs/modernization/roadmap.md`
Steps:
- Add this tracker.
- Link it from the roadmap.
- Make the scorecard the source for percentage claims.
Done Checks:
- `docs/modernization/roadmap.md` points agents to this file.
- The tracker has task states, scoring rules, and at least one ready queue.
Validation:
```powershell
git diff -- docs\modernization\roadmap.md docs\modernization\tasks.md
```
### ADP-001 - Remove History Bridge From Document Resize And Canvas Clear
Status: Done
Score: +1 legacy adapter retirement
Debt: `DEBT-0020`, `DEBT-0027`
Scope: `src/legacy_document_canvas_services.*`,
`src/app_core/document_resize.h`, `tests/app_core/document_resize_tests.cpp`,
related canvas-clear tests only
Goal:
Make document resize and canvas-clear execution consume app-core history
commands directly instead of routing through `legacy_history_services`.
Done Checks:
- `src/legacy_document_canvas_services.*` no longer includes
`legacy_history_services.h`.
- Resize still executes in order: resize, title update, history clear.
- Canvas clear still records undo and marks the document unsaved when a canvas
exists.
- `docs/modernization/debt.md` narrows or closes the affected removal condition.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_resize|pp_app_core_document_canvas|pano_cli_plan_document_resize|pano_cli_plan_canvas_clear" --output-on-failure
cmake --build --preset windows-msvc-default --config Debug --target PanoPainter pano_cli
```
### ADP-002 - Remove History Bridge From Layer Operations
Status: Done
Score: +1 legacy adapter retirement
Debt: `DEBT-0021`
Scope: `src/legacy_document_layer_services.*`,
`src/app_core/document_layer.h`, `tests/app_core/document_layer_tests.cpp`
Goal:
Move layer add/remove/merge/clear/rename history side effects into tested
app-core execution plans so the live layer bridge no longer calls
`legacy_history_services`.
Done Checks:
- `src/legacy_document_layer_services.*` no longer includes
`legacy_history_services.h`.
- Layer operations still preserve undo/history behavior covered by
`pp_app_core_document_layer_tests`.
- `pano_cli plan-layer-operation`, `plan-layer-menu`, and
`plan-layer-rename` JSON remains compatible.
- `docs/modernization/debt.md` records the narrowed or closed layer-history
adapter dependency.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_layer|pano_cli_plan_layer" --output-on-failure
cmake --build --preset windows-msvc-default --config Debug --target PanoPainter pano_cli
```
### ADP-003 - Remove History Bridge From Document Open And Session Save
Status: Done
Score: +1 legacy adapter retirement
Debt: `DEBT-0039`, `DEBT-0040`, `DEBT-0042`
Scope: `src/legacy_document_open_services.*`,
`src/legacy_document_session_services.*`,
`src/app_core/document_session.*`, `src/app_core/document_route.*`,
matching tests only
Goal:
Make document-open, close, save, save-before-workflow, Save As, and Save Version
history effects explicit app-core outputs instead of direct
`legacy_history_services` calls in the live bridges.
Done Checks:
- `src/legacy_document_open_services.*` and
`src/legacy_document_session_services.*` no longer include
`legacy_history_services.h`.
- Existing dirty-document, save-before, new-document, Save As, and Save Version
plans preserve their JSON contracts.
- The debt log is updated for every debt id listed above.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_route|pp_app_core_document_session|pano_cli_plan_open_route|pano_cli_simulate_app_session|pano_cli_plan_document_file|pano_cli_plan_document_version" --output-on-failure
cmake --build --preset windows-msvc-default --config Debug --target PanoPainter pano_cli
```
### ADP-004 - Make Dialog Creation A UI Factory Boundary
Status: Done
Score: +2 legacy adapter retirement
Debt: `DEBT-0058`, `DEBT-0063`
Scope: `src/legacy_app_dialog_services.*`,
`src/legacy_ui_overlay_services.*`, `src/app_dialogs.cpp`,
`src/app_core/app_dialog.h`, dialog tests only
Goal:
Keep app-core dialog metadata pure, but move retained
`NodeProgressBar`/`NodeMessageBox`/`NodeInputBox` construction behind one
`pp_panopainter_ui` or retained UI factory function. `App` should ask for a
dialog object through an interface instead of knowing individual node creation
details.
Done Checks:
- `App::show_progress`, `App::message_box`, and `App::input_box` still preserve
captions, cancel behavior, and keyboard behavior.
- New factory path has focused tests or existing `pp_app_core_app_dialog_tests`
plus a smoke command proving the live adapter still builds.
- The debt log states exactly which raw-node lifetime hazards remain.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_app_core_app_dialog|pano_cli_plan_app_dialog" --output-on-failure
cmake --build --preset windows-msvc-default --config Debug --target PanoPainter pano_cli
```
### ADP-005 - Convert One Popup/Dialog Family To Checked Overlay Lifetime
Status: Done
Score: +2 legacy adapter retirement
Debt: `DEBT-0063`
Scope: choose exactly one family from `src/node_dialog_open.cpp`,
`src/node_dialog_browse.cpp`, `src/node_panel_quick.cpp`,
`src/node_panel_stroke.cpp`, or `src/node_combobox.cpp`, plus
`src/legacy_ui_overlay_services.*`
Goal:
Adopt `pp_ui_core` overlay lifetime semantics for one retained popup/dialog
family before trying to rewrite all retained UI nodes.
Done Checks:
- The chosen family no longer owns open-coded root insertion, outside-click
release, close callback wiring, or destroy-during-callback behavior.
- Existing UI behavior is preserved.
- Tests cover missing root/template handling and close/release behavior for the
chosen family.
- The debt log names the completed family and the remaining families.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_ui_core_overlay_lifetime|pp_ui_core_node_lifetime" --output-on-failure
cmake --build --preset windows-msvc-default --config Debug --target PanoPainter
```
### RND-001 - Make Pure Equirectangular Export The Primary Success Path
Status: Done
Score: +2 renderer boundary and OpenGL parity
Debt: `DEBT-0010`, `DEBT-0036`, `DEBT-0043`
Scope: `src/legacy_document_export_services.*`,
`src/app_core/document_export.*`, `src/paint_renderer/compositor.*`,
`tests/app_core/document_export_tests.cpp`,
`tests/paint_renderer/compositor_tests.cpp`
Goal:
For payload-complete snapshots, live PNG/JPEG equirectangular export should
complete through the pure document/paint-renderer writer. Retained
`Canvas::export_equirectangular*` should run only for unsupported Web,
incomplete-readback, or writer-failure fallbacks.
Done Checks:
- Export route reports whether the pure writer was used or why fallback was
required.
- Tests cover pure-writer success and each fallback reason.
- Live bridge does not call retained equirectangular export after pure-writer
success.
- The debt log narrows retained equirectangular export execution.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_export|pp_paint_renderer_compositor|pano_cli_plan_export_snapshot_route|pano_cli_simulate_document_export" --output-on-failure
cmake --build --preset windows-msvc-default --config Debug --target PanoPainter pano_cli
```
### RND-002 - Make Pure Layer And Animation Collection Export Primary
Status: Done
Score: +2 renderer boundary and OpenGL parity
Debt: `DEBT-0010`, `DEBT-0036`, `DEBT-0043`
Scope: `src/legacy_document_export_services.*`,
`src/app_core/document_export.*`, `src/paint_renderer/compositor.*`,
collection export tests only
Goal:
For payload-complete snapshots, live layer and animation-frame collection export
should complete through the pure collection writer. Retained
`Canvas::export_layers*` and `Canvas::export_anim_frames*` should run only for
unsupported Web, incomplete-readback, or writer-failure fallbacks.
Done Checks:
- Collection export route reports pure-writer success versus fallback reason.
- Tests cover layer collection, animation-frame collection, and fallback.
- The debt log narrows retained collection export execution.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_export|pp_paint_renderer_compositor|pano_cli_plan_export_snapshot_route" --output-on-failure
cmake --build --preset windows-msvc-default --config Debug --target PanoPainter pano_cli
```
### RND-003 - Replace Depth Export Readiness With Pure Depth Export Execution
Status: Done
Score: +3 renderer boundary and OpenGL parity
Debt: `DEBT-0010`, `DEBT-0036`, `DEBT-0043`
Scope: `src/paint_renderer/compositor.*`,
`src/app_core/document_export.*`, `src/legacy_document_export_services.*`,
depth tests only
Goal:
Turn the current pure depth export render plan into actual payload generation
for payload-complete snapshots, then write image/depth payloads through the
existing app-core two-payload writer before falling back to retained
`Canvas::export_depth*`.
Done Checks:
- Pure depth export produces deterministic image and depth payloads for a
payload-complete snapshot.
- Retained depth export runs only for unsupported targets, incomplete readback,
or writer failure.
- Tests cover malformed depth target inputs and byte-size validation.
- The debt log narrows depth export readback/execution.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_export|pp_paint_renderer_compositor|pano_cli_plan_export_snapshot_route" --output-on-failure
cmake --build --preset windows-msvc-default --config Debug --target PanoPainter pano_cli
```
### RND-004 - Add First Desktop GPU Golden Gate
Status: Done
Score: +2 hardening and future backend readiness
Debt: `DEBT-0036`
Scope: `tests/`, `CMakeLists.txt`, renderer test helpers only
Goal:
Create the first non-default `desktop-gpu` golden/readback test that validates
one OpenGL output against a deterministic fixture. Keep it opt-in so headless
agents are not blocked.
Done Checks:
- `ctest --preset desktop-gpu --build-config Debug` has at least one real test.
- The test is skipped with a clear message when no GPU/context is available.
- The roadmap and debt log describe what the golden covers and what remains.
Validation:
```powershell
ctest --preset desktop-gpu --build-config Debug --output-on-failure
ctest --preset desktop-fast --build-config Debug -R "pp_renderer_gl|pp_paint_renderer" --output-on-failure
```
### RND-005 - Add Desktop GPU Preview Golden Gate
Status: Done
Score: +2 hardening and future backend readiness
Debt: `DEBT-0036`
Scope: `tests/`, `CMakeLists.txt`, renderer test helpers only
Goal:
Create the next non-default `desktop-gpu` golden/readback test for the preview
parity lane so the renderer parity coverage is not limited to the existing
clear-readback fixture.
Done Checks:
- `ctest --preset desktop-gpu --build-config Debug` has more than one real GPU
readback gate.
- The test is skipped with a clear message when no GPU/context is available.
- The roadmap and debt log describe the GPU readback coverage and what remains.
Validation:
```powershell
ctest --preset desktop-gpu --build-config Debug --output-on-failure
ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_compositor|pp_paint_renderer_stroke_execution" --output-on-failure
```
Completed Task Log:
| Date | Task | Score | Validation | Commit |
| --- | --- | ---: | --- | --- |
| 2026-06-13 | RND-005 | +2 hardening and future backend readiness | `ctest --preset desktop-gpu --build-config Debug --output-on-failure`; `ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_compositor|pp_paint_renderer_stroke_execution" --output-on-failure` | `pending` |
### PLT-001 - Split Apple Picker/Browse Service From Legacy Platform Adapter
Status: Done
Score: +2 platform and package parity
Debt: `DEBT-0017`, `DEBT-0051`, `DEBT-0055`
Scope: `src/platform_legacy/legacy_platform_services.*`, new
`src/platform_apple/*` files if needed, `CMakeLists.txt`, platform API tests
Goal:
Move macOS/iOS document browse roots, file picking, directory picking, and
display-path formatting out of the catch-all legacy platform adapter and into a
named Apple platform service boundary.
Done Checks:
- `src/platform_legacy/legacy_platform_services.*` no longer owns Apple
browse/picker policy.
- Apple compile validation still passes through the remote build script.
- The debt log narrows Apple platform shell extraction.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests --output-on-failure
powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.ps1 -Presets macos,ios-simulator,ios-device
```
### PLT-002 - Split Web Export/Storage Policy From Legacy Platform Adapter
Status: Done
Score: +2 platform and package parity
Debt: `DEBT-0050`, `DEBT-0053`, `DEBT-0057`
Scope: `src/platform_legacy/legacy_platform_services.*`, new
`src/platform_web/*` files if needed, `src/platform_api/*`, platform tests
Goal:
Move WebGL exported-image publishing, persistent-storage flushing,
prepared-file handoff, and default canvas resolution out of the catch-all
legacy platform adapter into a named Web platform service boundary.
Done Checks:
- Web policy is injectable through `pp_platform_api`.
- The legacy adapter no longer owns Web default canvas resolution or storage
flush policy.
- Package-smoke readiness still reports Web blockers explicitly.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests --output-on-failure
powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -ReadinessOnly
```
### DEP-001 - Remove Generated fmt Overlay
Status: Done
Score: +1 build and CMake ownership
Debt: `DEBT-0062`
Scope: `CMakeLists.txt`, `cmake/`, `vcpkg.json`, `libs/fmt` or package wiring
Goal:
Use a supported fmt package or update the vendored fmt release so VS 2026 no
longer needs a generated `format.h` overlay.
Done Checks:
- No build-tree fmt header overlay is generated.
- `DEBT-0062` is closed.
- Windows app and at least one focused component test build pass.
Validation:
```powershell
cmake --build --preset windows-msvc-default --config Debug --target PanoPainter pp_platform_api_tests
ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests --output-on-failure
```
### DEP-002 - Remove Generated nanort Overlay
Status: Done
Score: +1 build and CMake ownership
Debt: `DEBT-0060`
Scope: retained Android package CMake, `libs/nanort`, grid/lightmap dependency
wiring
Goal:
Update, replace, or isolate `nanort` so Android package builds do not generate
a patched vendor overlay.
Done Checks:
- Android retained package CMake no longer generates a patched `nanort.h`.
- `DEBT-0060` is closed.
- Standard Android retained package validation passes.
Validation:
```powershell
powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages standard
cmake --build --preset windows-msvc-default --config Debug --target PanoPainter
```
## Blocked Or Later Queue
These are real goals, but they should not be picked until prerequisite tasks
above have reduced risk.
### LATER-001 - Replace OpenVR With OpenXR
Status: Blocked
Score: +3 platform and package parity
Debt: `DEBT-0061`
Blocked By: OpenXR package decision and runtime availability
Done Checks:
- OpenXR SDK/package target exists.
- Windows platform service can start/stop desktop XR without OpenVR.
- `libs/openvr` and `openvr_api.dll` deployment are removed.
- `DEBT-0061` is closed.
### LATER-002 - Remove Catch2 Harness Debt
Status: Blocked
Score: +2 test and automation coverage
Debt: `DEBT-0005`
Blocked By: vcpkg/toolchain reliability across Windows and headless presets
Done Checks:
- Existing local test harness is replaced or permanently justified.
- Catch2 tests run through `desktop-fast` and vcpkg headless presets.
- `DEBT-0005` is closed.
### LATER-003 - Live Stroke Rasterization Through Renderer Services
Status: Ready
Score: +5 renderer boundary and OpenGL parity
Debt: `DEBT-0036`
Done Checks:
- Live stroke rasterization, dual-brush compositing, and pattern feedback choose
paths through renderer services.
- OpenGL output parity is covered by golden/readback tests.
- Retained stroke OpenGL execution is deleted or isolated as an OpenGL backend
implementation.
Progress Notes:
- 2026-06-13: `Canvas::stroke_draw()` live-pass sampler wiring now reuses a
retained helper builder, and the stroke execution tests cover it. `STR-004`
is now done after the final pad-destination helper extraction and tracker
closeout.
- 2026-06-13: `STR-004` closed the last inline stroke dispatch glue. `LATER-003`
remains at the binding-only tail.
- 2026-06-13: `Canvas::stroke_draw_samples()` now reuses a retained destination
texture dispatch helper for the live sample path; `Canvas` still owns the
concrete face textures and callback execution.
- 2026-06-13: `pp_paint_renderer_stroke_execution_tests` now also covers the
new retained main-pass texture dispatch helper builder and its pattern/mixer
wiring. Next slice should target another narrow `stroke_draw()` seam or stop
once the remaining inline code is just trivial binding glue.
- 2026-06-13: `pp_paint_renderer_stroke_execution_tests` now covers the new
retained sampler dispatch helper builder and its pattern bind/unbind wiring.
Next slice should target another narrow `stroke_draw()` seam or another
helper-builder regression without reopening landed texture or dual-pass
helpers.
- 2026-06-13: `Canvas::stroke_draw()` live-pass sampler dispatch now reuses a
retained helper builder for brush-tip, destination, pattern, and mixer
sampler callbacks; the live adapter still owns the concrete sampler objects.
Next slice should target another narrow `stroke_draw()` seam without
reopening landed texture or dual-pass helpers.
- 2026-06-13: `Canvas::stroke_draw()` main-pass texture dispatch now reuses
retained helper builders for texture-unit, brush-tip, pattern, and mixer
callback wiring; the live adapter still owns the concrete textures and
sampler state. Next slice should target the remaining inline `stroke_draw()`
dispatch construction or another narrow seam without reopening landed pad or
dual-pass helpers.
- 2026-06-13: `Canvas::stroke_draw()` dual-brush replay now reuses the new
retained brush-tip texture dispatch helper for binding and unbinding; the
live adapter still owns the concrete brush texture object and live-pass
execution. Next slice should target another narrow `stroke_draw()` seam
without reopening landed pad or sample helpers.
- 2026-06-13: `Canvas::stroke_draw()` dual-brush replay now routes shader
setup, brush-tip texture binding, live-pass execution, and brush-tip
unbinding through `execute_legacy_canvas_stroke_dual_pass(...)`; the live
adapter still owns the concrete texture callbacks and face-frame replay.
Next slice should target another narrow `stroke_draw()` seam without
reopening landed pad or sample helpers.
- 2026-06-13: `Canvas::stroke_draw()` now routes the pad-stroke destination
texture dispatch through a local helper lambda, so the repeated bind/unbind
callback construction is centralized while the pad executor still owns the
face loop and copy timing. Next slice should target another narrow canvas
stroke execution seam without reopening landed pad or sample helpers.
- 2026-06-13: `Canvas::stroke_draw_mix()` now routes mix-pass plane planning
through `plan_legacy_canvas_stroke_mix_pass_planes(...)` and wraps retained
framebuffer setup/teardown with
`execute_legacy_canvas_stroke_mix_pass_with_setup(...)`; the live adapter
still owns concrete sampler, texture, and draw callbacks. `pp_paint_renderer_stroke_execution_tests`
now covers the default dirty-union path for live-pass face-framebuffer
execution. Next slice should target the remaining Canvas stroke execution
seam without reopening the landed mix-pass setup helper.
- 2026-06-13: `Canvas::draw_merge()` now routes per-plane merge-target clear,
blend-state gating, and optional checkerboard prepass through
`execute_legacy_canvas_draw_merge_plane_setup(...)`; per-layer iteration,
temporary-stroke branches, and concrete framebuffer ownership remain local to
`Canvas`. Next slice should target another narrow draw-merge execution seam
without reopening landed plane-setup, temporary-composite, layer-blend,
final-plane, or texture-alpha helpers.
- 2026-06-13: `Canvas::draw_merge()` now routes the standard per-layer
texture-alpha pass through
`execute_legacy_canvas_draw_merge_layer_texture(...)`; temporary-stroke
branches, per-plane iteration, and concrete RTT ownership remain local to
`Canvas`. Next slice should target another narrow draw-merge execution seam
without reopening landed temporary-composite, layer-blend, final-plane, or
texture-alpha helpers.
- 2026-06-13: `pp_paint_renderer_stroke_execution_tests` now also covers
retained frame-plan assembly for previous-sample projection mode and zoom
scaling. Next slice should target the remaining preview/Canvas stroke
execution seam or another narrow renderer boundary without reopening the
landed stroke-frame planner coverage.
- 2026-06-13: `NodeStrokePreview` live-pass orchestration now routes through
`execute_legacy_stroke_preview_live_pass(...)`, and
`pp_paint_renderer_stroke_execution_tests` now covers live-pass clear,
traversal, and final copy ordering. Next slice should target the remaining
preview or Canvas stroke execution seam without reopening the landed live-pass
helper.
- 2026-06-13: `NodeStrokePreview::draw_stroke_immediate()` now routes retained
preview feedback/material/composite planning plus stroke-shader uniform
assembly through `plan_legacy_node_stroke_preview_pass_orchestration(...)`;
compositor coverage now locks destination-feedback fallback, composite-slot
intent, and the retained pattern/dual shader-uniform handoff. The preview
node still owns brush object mutation, retained `Stroke` population, and the
concrete GL pass callbacks. Next slice should target another narrow preview
execution seam without reopening the landed preview setup, mix, pass-sequence,
or final-composite helpers.
- 2026-06-13: `NodeStrokePreview::draw_stroke_immediate()` now routes preview
stroke max-size fallback, dual-preview max-size derivation, pattern-scale
flips, and Bezier preview-point generation through
`plan_legacy_node_stroke_preview_stroke_setup(...)`; compositor coverage now
also locks the retained stroke-setup curve/pressure intent and
pass-sequence request-validation short-circuit behavior. The preview node
still owns brush object mutation, camera wiring, retained `Stroke`
population, and live GL execution. Next slice should target the remaining
higher-level preview pass orchestration seam without reopening landed
sample, mix, pass-sequence, or final-composite helpers.
- 2026-06-13: `Canvas::draw_merge()` per-layer blend composite now routes
merge-RTT unbind, shader setup, optional destination-copy feedback, draw,
and cleanup ordering through
`execute_legacy_canvas_draw_merge_layer_blend(...)`; temporary-stroke
branch selection, layer iteration, and final merged-plane redraw ownership
remain local to `Canvas`. Next slice should target the remaining end-of-plane
merged-texture copy/grid/final-redraw seam or another similarly narrow final
composite boundary without reopening landed temporary-composite or
per-layer blend helpers.
- 2026-06-13: `Canvas::draw_merge()` end-of-plane merged-texture copy,
optional checkerboard redraw, and final merged-texture composite ordering now
route through `execute_legacy_canvas_draw_merge_final_plane_composite(...)`;
per-plane iteration and concrete framebuffer, sampler, texture, and draw
callbacks remain local to `Canvas`. Next slice should target another narrow
retained draw-merge boundary without reopening landed temporary-composite,
layer-blend, or final-plane helpers.
- 2026-06-13: `Canvas::draw_merge()` erase live temporary-stroke composite now
routes retained setup, sampler bind, texture bind, draw, and texture unbind
ordering through `execute_legacy_canvas_stroke_temporary_composite(...)`;
both live temporary composite branches now share the same retained execution
helper while `Canvas` keeps only concrete GL object callbacks and broader
final composite ownership. Next slice should target the remaining final
composite seam without reopening landed sample, mix, dirty, or framebuffer
helpers.
- 2026-06-13: `NodeStrokePreview::stroke_draw_mix()` now routes retained
mix-pass shader setup plus framebuffer/state/input/draw ordering through
`execute_legacy_node_stroke_preview_mix_pass(...)`, with compositor coverage
locking the retained callback order and shader-plan handoff. The preview node
keeps only the concrete GL save/restore, texture-object bind, and plane-draw
callbacks. Next slice should target another retained preview or canvas stroke
seam without reopening the landed preview sample, material-planning,
pass-sequence, or final-composite helpers.
- 2026-06-13: `NodeStrokePreview::draw_stroke_immediate()` now routes
dual-pass/background/main-pass/final-composite/copy-back ordering through
`execute_legacy_node_stroke_preview_pass_sequence(...)`; the remaining local
preview ownership is concentrated around `stroke_draw_mix()` setup/execution.
Next slice should target that final mix-pass setup/execution seam without
reopening landed sample, binding, material-planning, or final-composite
helpers.
- 2026-06-13: `Canvas::draw_merge()` non-erase live temporary-stroke
composite ordering now routes through
`execute_legacy_canvas_stroke_temporary_composite(...)`; erase-path and
broader final composite ownership still remain local to `Canvas`. Next slice
should target the erase temporary composite branch or another similarly
narrow final composite seam without reopening landed sample, mix, dirty, or
framebuffer helpers.
- 2026-06-13: `Canvas::stroke_draw_samples()` now routes face-indexed
destination bind/copy/unbind and brush upload/draw through
`execute_legacy_canvas_stroke_face_sample_polygon(...)`; the retained sample
executor now owns the face-aware dispatch contract while `Canvas` keeps only
the concrete GL object callbacks. Next slice should target any remaining
final temporary-texture composite setup without reopening landed sample,
mix, sampler, dirty, face, or pad helpers.
- 2026-06-13: `pp_paint_renderer_stroke_execution_tests` now also covers
direct retained frame-sample callback ordering plus
`execute_legacy_canvas_stroke_frame_samples_with_dirty_tracking(...)`
timing/state semantics, including pre-update dirty visibility and
`previous_pass_dirty_box` override behavior. Next test slice should target
the next header-level preview live sample ordering surface without reopening
production files.
- 2026-06-13: `Canvas::stroke_draw_mix()` now routes visible-plane filtering,
retained sampler/texture-slot binding, and final plane draw ordering through
`execute_legacy_canvas_stroke_mix_pass(...)`; mixer framebuffer/state setup
and per-plane shader material/MVP preparation remain local to `Canvas`. Next
slice should target the remaining `stroke_draw_samples()` callback body or
any final temporary-texture composite setup without reopening landed
sample, sampler, dirty, face, or pad helpers.
- 2026-06-13: `pp_paint_renderer_stroke_execution_tests` now also covers
retained preview background capture ordering, final composite ordering, and
preview texture-copy bind-before-copy behavior via
`legacy_canvas_stroke_preview_services.h`. Next test slice should target the
next header-level preview live sample or mix-pass ordering surface without
reopening production files.
- 2026-06-13: `Canvas::stroke_draw_samples()` now routes polygon
triangulation, sample-point assembly, and the retained destination-copy /
upload / draw helper handoff through
`execute_legacy_canvas_stroke_sample_polygon(...)`; direct GL callback
wiring and the remaining live draw ownership stay local to `Canvas`. Next
slice should target the remaining callback body or final temporary-texture
composite setup without reopening the landed sampler, dirty, face, or pad
helpers.
- 2026-06-13: `NodeStrokePreview::stroke_draw_mix()` now routes mixer
framebuffer bind/unbind, viewport/scissor/blend state, texture-slot binding,
and final plane draw through one local helper; material planning and shader
uniform setup remain local to the preview node. Next slice should target the
remaining mix-pass material/setup orchestration without reopening the landed
preview live-pass, binding, sample, or final composite helpers.
- 2026-06-13: `NodeStrokePreview::stroke_draw_mix()` now routes retained
mix-pass material planning plus composite/pattern/dual uniform payload
assembly through `plan_legacy_node_stroke_preview_mix_pass(...)`, with
compositor coverage locking the retained preview mix adapter semantics for
composite-pass patterning and dual state. Mixer framebuffer ownership,
retained shader execution, texture binding, and final draw/copy ordering
remain local to the preview node. Next slice should target the remaining
mix-pass execution ownership without reopening the landed preview live-pass,
binding, sample, or final composite helpers.
- 2026-06-13: `pp_paint_renderer_stroke_execution_tests` now also covers
retained texture-dispatch activation order and sampler-dispatch routing
across brush tip, destination, pattern, and mixer helper inputs. Next test
slice should extend the dedicated lane into frame-sample loop ordering or
preview-side retained helper coverage.
- 2026-06-13: Added `pp_paint_renderer_stroke_execution_tests` as a dedicated
retained stroke execution-helper target covering texture-input binding order,
sample destination-copy behavior, live-pass face-framebuffer dirty tracking,
and pad-face destination-copy behavior. Next test slice should extend this
target toward the newer sampler-dispatch helpers or preview-side retained
execution helpers.
- 2026-06-13: `Canvas::stroke_draw` live-pass sampler bind/unbind plus
semantic texture-input dispatch now routes through retained stroke execution
helpers; concrete GL object mapping, framebuffer ownership, shader timing,
and final draw execution remain local to `Canvas`. Next slice should target
the remaining live draw execution boundary inside `stroke_draw_samples()` or
the final temporary-texture composite setup without reopening the landed
dirty, face-framebuffer, pad, or texture-intent helpers.
- 2026-06-13: `NodeStrokePreview::stroke_draw_samples()` now routes
destination bind/unbind, framebuffer copy callback wrapping, sample-point
assembly, and brush-vertex upload/draw through one local helper; mixer-pass
state execution and higher-level pass orchestration remain local to the
preview node. Next slice should target the remaining mixer-pass state/copy
ordering without reopening the landed preview live-pass, binding, or final
composite helpers.
- 2026-06-13: `Canvas::stroke_draw` main, pad, and dual live-pass
texture-input binding/unbinding intent now routes through retained stroke
execution helpers; sampler binding, concrete GL object mapping, framebuffer
ownership, and final draw execution remain local to `Canvas`. Next slice
should target the remaining sampler binding/teardown or the direct
semantic-input-to-GL mapping callbacks without reopening the landed dirty,
face-framebuffer, or pad helpers.
- 2026-06-13: `NodeStrokePreview::draw_stroke_immediate()` live-pass sampler
binding, dual/main pass texture binding, checkerboard/background capture
wrapping, and final preview copy-back now route through named local helpers;
mixer state execution and per-sample GL ordering remain local to the preview
node. Next slice should target the remaining mixer-pass state/copy ordering
or sample-pass destination callback wrapping without reopening the landed
preview live-pass or final-composite helpers.
- 2026-06-13: `NodeStrokePreview::draw_stroke_immediate()` dual-pass and
main-pass frame-loop execution plus full-frame copy-back now route through
shared local helpers; checkerboard/background capture ordering, texture-unit
binding, mixer ownership, and final composite semantics remain local to the
preview node. Next slice should target background/final-copy helpers or
pass-level texture binding without reopening the new preview composite
helper.
- 2026-06-13: `Canvas::stroke_draw` main and dual live-pass per-face
framebuffer begin/end execution plus pad-face array assembly now route
through `legacy_canvas_stroke_execution_services.h`; shader activation
timing, texture/sampler binding, framebuffer ownership, and final draw
execution remain local to `Canvas`. Next slice should target the remaining
pass-level texture binding or final composite setup without reopening the
newer pad, dirty, or commit helpers.
- 2026-06-13: `Canvas::stroke_draw` current and dual stroke per-face
framebuffer/sample callback ordering now routes through
`legacy_canvas_stroke_execution_services.h`; framebuffer ownership, shader
uniform timing, sampler/texture binding, and draw execution remain local to
Canvas. Next slice should target the remaining per-face retained state around
sample execution without reopening the new pad-pass, dirty-mutation, or
commit executors.
- 2026-06-13: `Canvas::stroke_draw` current and dual stroke dirty-box mutation
now routes through `legacy_canvas_stroke_execution_services.h`; framebuffer
binding, shader uniform timing, sampler/texture binding, and draw execution
remain local to Canvas. Next slice should wrap the retained per-face
framebuffer/sample callback ordering without rewriting the new pad-pass or
commit executors.
- 2026-06-13: `Canvas::stroke_commit` now routes retained commit input
texture/sampler binding, erase/composite draw dispatch, committed-copy, and
dilate draw through `legacy_canvas_stroke_commit_services.h`; history
readback, `ActionStroke` population, and layer dirty-box mutation remain
local to Canvas.
- 2026-06-13: `pp_paint_renderer` owns tested Canvas stroke commit sequencing
and commit texture slot intent. Next slice should wire
`legacy_canvas_stroke_commit_services.h` into `Canvas::stroke_commit` while
keeping history/layer mutation local.
### LATER-004 - Remove Catch-All Platform Legacy Adapter
Status: Blocked
Score: +5 platform and package parity
Debt: `DEBT-0017`
Blocked By: Apple/Web split tasks and per-platform package validation
Done Checks:
- `src/platform_legacy/legacy_platform_services.*` is deleted or contains only
compile-time unsupported stubs with debt entries.
- Windows, Apple, Android, Linux, and Web platform services are named targets.
- Platform-build and package-smoke report explicit pass/fail per platform.
## Completed Task Log
| Date | Task | Score Change | Validation | Commit |
| --- | --- | ---: | --- | --- |
| 2026-06-12 | RND-004 | +2 hardening and future backend readiness | `ctest --preset desktop-gpu --build-config Debug --output-on-failure`; `ctest --preset desktop-fast --build-config Debug -R "pp_renderer_gl\|pp_paint_renderer" --output-on-failure` | e37b2929 |
| 2026-06-12 | DEP-002 | +1 build and CMake ownership | `powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages standard` | 648404ee |
| 2026-06-12 | RND-002 | +2 renderer boundary and OpenGL parity | `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_export\|pp_paint_renderer_compositor\|pano_cli_plan_export_snapshot_route\|pano_cli_simulate_document_export" --output-on-failure` | 46fb8ef |
| 2026-06-12 | RND-001 | +2 renderer boundary and OpenGL parity | `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_export\|pp_paint_renderer_compositor\|pano_cli_plan_export_snapshot_route\|pano_cli_simulate_document_export" --output-on-failure` | 46fb8ef |
| 2026-06-12 | ADP-004 | +2 legacy adapter retirement | VS-bundled CMake build of `pp_app_core_app_dialog_tests` and `pano_cli`; `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_app_dialog\|pano_cli_plan_app_dialog" --output-on-failure` | 46fb8ef |
| 2026-06-12 | PLT-001 | +2 platform and package parity | VS-bundled CMake build of `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests --output-on-failure`; Apple remote build blocked by unpublished fmt submodule pointer before DEP-001 correction | 46fb8ef |
| 2026-06-12 | RND-003 | +3 renderer boundary and OpenGL parity | VS-bundled CMake build of `pp_paint_renderer_compositor_tests`, `pp_app_core_document_export_tests`, and `pano_cli`; `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_export\|pp_paint_renderer_compositor\|pano_cli_plan_export_snapshot_route" --output-on-failure` | 46fb8ef |
| 2026-06-12 | DEP-001 | +1 build and CMake ownership | VS-bundled CMake build of `PanoPainter` and `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests --output-on-failure` | 46fb8ef |
| 2026-06-12 | ADP-003 | +1 legacy adapter retirement | `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_route\|pp_app_core_document_session\|pano_cli_plan_open_route\|pano_cli_simulate_app_session\|pano_cli_plan_document_file\|pano_cli_plan_document_version" --output-on-failure` | 34a9e910 |
| 2026-06-12 | PLT-002 | +2 platform and package parity | `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests --output-on-failure`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -ReadinessOnly`; `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_layer\|pano_cli_plan_layer\|pp_platform_api_tests" --output-on-failure` | 8cd38401 |
| 2026-06-12 | ADP-002 | +1 legacy adapter retirement | `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_layer\|pano_cli_plan_layer" --output-on-failure`; `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_layer\|pano_cli_plan_layer\|pp_platform_api_tests" --output-on-failure` | ae242852 |
| 2026-06-12 | ADP-001 | +1 legacy adapter retirement | `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_resize\|pp_app_core_document_canvas\|pano_cli_plan_document_resize\|pano_cli_plan_canvas_clear" --output-on-failure`; `powershell -ExecutionPolicy Bypass -File scripts\automation\quiet-validate.ps1 -BuildTargets PanoPainter,pano_cli -TestRegex "pp_app_core\|pano_cli_plan"` | e489b1e2 |
| 2026-06-12 | MT-001 | 0 | `git diff -- docs\modernization\roadmap.md docs\modernization\tasks.md` | same docs slice |
| 2026-06-13 | STR-004 | +1 renderer boundary and OpenGL parity | `ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_stroke_execution" --output-on-failure`; `MSBuild.exe out\build\windows-msvc-default\tests\pp_paint_renderer_stroke_execution_tests.vcxproj /p:Configuration=Debug /p:Platform=x64` | 5c03b130 |
## Task Template
Use this shape when adding a new task:
````markdown
### STR-004 - Collapse Remaining Stroke Dispatch Glue
Status: Done
Score: +1 renderer boundary and OpenGL parity
Debt: `DEBT-0036`
Scope: `src/canvas.cpp`, `src/legacy_canvas_stroke_execution_services.h`,
`tests/paint_renderer/stroke_execution_tests.cpp`
Goal:
Remove the last inline `Canvas::stroke_draw()` dispatch construction that is
still just wiring. Keep the live adapter owning concrete GL objects, but move
the remaining helper-builder glue into retained stroke-execution helpers or
delete it if the callsite can consume a smaller already-tested helper.
Done Checks:
- `Canvas::stroke_draw()` has no ad hoc `LegacyCanvasStroke*Dispatch` literal
blocks for the remaining live-pass wiring.
- The helper-builder coverage tests prove the remaining dispatch helpers route
the expected callbacks.
- `docs/modernization/debt.md` records the reduced stroke-dispatch surface.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_stroke_execution" --output-on-failure
& 'C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe' out\build\windows-msvc-default\tests\pp_paint_renderer_stroke_execution_tests.vcxproj /p:Configuration=Debug /p:Platform=x64
```
### STR-005 - Extract Live Stroke Face Callback Orchestration
Status: Done
Score: +1 renderer boundary and OpenGL parity
Debt: `DEBT-0036`
Scope: `src/canvas.cpp`, `src/legacy_canvas_stroke_execution_services.h`,
`tests/paint_renderer/stroke_execution_tests.cpp`
Goal:
Move the remaining inline live-pass face callback orchestration in
`Canvas::stroke_draw()` into a retained helper so the live stroke path owns only
concrete GL object wiring and per-face execution callbacks. Keep the face loop
and callback order unchanged.
Done Checks:
- `Canvas::stroke_draw()` no longer contains the remaining inline face-pass
callback block around `execute_legacy_canvas_stroke_live_pass_with_face_framebuffers(...)`.
- Regression coverage proves the extracted helper preserves callback order and
face-state updates.
- `docs/modernization/debt.md` records the reduced live-stroke callback surface.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_stroke_execution" --output-on-failure
& 'C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe' out\build\windows-msvc-default\tests\pp_paint_renderer_stroke_execution_tests.vcxproj /p:Configuration=Debug /p:Platform=x64
```
### Completed Task Log
| Date | Task | Score | Validation | Commit |
| --- | --- | ---: | --- | --- |
| 2026-06-13 | STR-005 | +1 renderer boundary and OpenGL parity | `ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_stroke_execution" --output-on-failure`; `MSBuild.exe out\build\windows-msvc-default\tests\pp_paint_renderer_stroke_execution_tests.vcxproj /p:Configuration=Debug /p:Platform=x64` | `4c9809f7` |
### STR-006 - Extract Dual Stroke Face Execution Orchestration
Status: Done
Score: +1 renderer boundary and OpenGL parity
Debt: `DEBT-0036`
Scope: `src/canvas.cpp`, `src/legacy_canvas_stroke_execution_services.h`,
`tests/paint_renderer/stroke_execution_tests.cpp`
Goal:
Move the inline dual-stroke face execution lambda in `Canvas::stroke_draw()`
into a retained helper so the dual-pass branch keeps only concrete shader,
texture, and framebuffer wiring. Preserve dual-pass face order and callback
behavior.
Done Checks:
- `Canvas::stroke_draw()` no longer contains the inline dual-pass face execution
lambda around `execute_legacy_canvas_stroke_live_pass_with_face_framebuffers(...)`.
- Regression coverage proves the extracted helper preserves dual-pass callback
order and face execution.
- `docs/modernization/debt.md` records the reduced dual-pass callback surface.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_stroke_execution" --output-on-failure
& 'C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe' out\build\windows-msvc-default\tests\pp_paint_renderer_stroke_execution_tests.vcxproj /p:Configuration=Debug /p:Platform=x64
```
### Completed Task Log
| Date | Task | Score | Validation | Commit |
| --- | --- | ---: | --- | --- |
| 2026-06-13 | STR-006 | +1 renderer boundary and OpenGL parity | `ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_stroke_execution" --output-on-failure`; `MSBuild.exe out\build\windows-msvc-default\tests\pp_paint_renderer_stroke_execution_tests.vcxproj /p:Configuration=Debug /p:Platform=x64` | `ae46be9f` |
### STR-007 - Extract Main Stroke Face Loop Orchestration
Status: Done
Score: +1 renderer boundary and OpenGL parity
Debt: `DEBT-0036`
Scope: `src/canvas.cpp`, `src/legacy_canvas_stroke_execution_services.h`,
`tests/paint_renderer/stroke_execution_tests.cpp`
Goal:
Move the inline main-stroke face loop in `Canvas::stroke_draw()` into a
retained helper so the main-pass branch keeps only concrete shader, sampler,
and framebuffer wiring. Preserve main-pass face order and callback behavior.
Done Checks:
- `Canvas::stroke_draw()` no longer contains the inline main-pass face loop
around `execute_legacy_canvas_stroke_live_pass_with_face_framebuffers(...)`.
- Regression coverage proves the extracted helper preserves main-pass callback
order and face execution.
- `docs/modernization/debt.md` records the reduced main-pass callback surface.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_stroke_execution" --output-on-failure
& 'C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe' out\build\windows-msvc-default\tests\pp_paint_renderer_stroke_execution_tests.vcxproj /p:Configuration=Debug /p:Platform=x64
```
### Completed Task Log
| Date | Task | Score | Validation | Commit |
| --- | --- | ---: | --- | --- |
| 2026-06-13 | STR-007 | +1 renderer boundary and OpenGL parity | `ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_stroke_execution" --output-on-failure`; `MSBuild.exe out\build\windows-msvc-default\tests\pp_paint_renderer_stroke_execution_tests.vcxproj /p:Configuration=Debug /p:Platform=x64` | `fee09e53` |
### STR-008 - Extract Pad Stroke Face Orchestration
Status: Done
Score: +1 renderer boundary and OpenGL parity
Debt: `DEBT-0036`
Scope: `src/canvas.cpp`, `src/legacy_canvas_stroke_execution_services.h`,
`tests/paint_renderer/stroke_execution_tests.cpp`
Goal:
Move the inline pad-stroke face execution block in `Canvas::stroke_draw()` into
a retained helper so the pad branch keeps only concrete brush-shape, texture,
and framebuffer wiring. Preserve pad-face order and copy timing.
Done Checks:
- `Canvas::stroke_draw()` no longer contains the inline pad-face block around
`execute_legacy_canvas_stroke_pad_faces(...)`.
- Regression coverage proves the extracted helper preserves pad-face order and
copy behavior.
- `docs/modernization/debt.md` records the reduced pad callback surface.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_stroke_execution" --output-on-failure
& 'C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe' out\build\windows-msvc-default\tests\pp_paint_renderer_stroke_execution_tests.vcxproj /p:Configuration=Debug /p:Platform=x64
```
### Completed Task Log
| Date | Task | Score | Validation | Commit |
| --- | --- | ---: | --- | --- |
| 2026-06-13 | STR-008 | +1 renderer boundary and OpenGL parity | `ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_stroke_execution" --output-on-failure`; `MSBuild.exe out\build\windows-msvc-default\tests\pp_paint_renderer_stroke_execution_tests.vcxproj /p:Configuration=Debug /p:Platform=x64` | `11a62e9b` |
### STR-009 - Extract Stroke Commit Callback Orchestration
Status: Done
Score: +2 renderer boundary and OpenGL parity
Debt: `DEBT-0036`
Scope: `src/canvas.cpp`, `src/legacy_canvas_stroke_execution_services.h`,
`tests/paint_renderer/stroke_execution_tests.cpp`
Goal:
Move the large inline callback block in `Canvas::stroke_commit()` into a
retained helper so commit sequencing keeps only concrete layer/texture/canvas
wiring. Preserve commit ordering, history capture, and dilate sequencing.
Done Checks:
- `Canvas::stroke_commit()` no longer contains the large inline commit callback
block around `execute_legacy_canvas_stroke_commit_sequence(...)`.
- Regression coverage proves the extracted helper preserves commit ordering and
history capture.
- `docs/modernization/debt.md` records the reduced stroke-commit callback
surface.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_stroke_execution" --output-on-failure
& 'C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe' out\build\windows-msvc-default\tests\pp_paint_renderer_stroke_execution_tests.vcxproj /p:Configuration=Debug /p:Platform=x64
```
Completed Task Log:
| Date | Task | Score | Validation | Commit |
| --- | --- | ---: | --- | --- |
| 2026-06-13 | STR-009 | +2 renderer boundary and OpenGL parity | `ctest --preset desktop-fast --build-config Debug -R "retained_stroke_commit_callback_builder_preserves_order|retained_stroke_commit_runner_preserves_per_face_step_order" --output-on-failure`; `& 'C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe' out\build\windows-msvc-default\tests\pp_paint_renderer_compositor_tests.vcxproj /p:Configuration=Debug /p:Platform=x64` | `pending` |
### STR-011 - Extract Preview Main Pass Orchestration
Status: Done
Score: no score movement
Debt: `DEBT-0036`
Scope: `src/node_stroke_preview.cpp`, `tests/paint_renderer/compositor_tests.cpp`
Goal:
Extract the remaining `NodeStrokePreview::draw_stroke_immediate()` main-pass
orchestration into a retained helper boundary without changing preview output.
Done Checks:
- Main-pass live execution uses a retained helper for setup, frame execution,
and final texture copy.
- Preview regression coverage proves callback order and binding still match the
current behavior.
- `DEBT-0036` records the narrowed preview orchestration surface.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_compositor|pp_paint_renderer_stroke_execution" --output-on-failure
cmake --build --preset windows-msvc-default --config Debug --target PanoPainter
```
Completed Task Log:
| Date | Task | Score | Validation | Commit |
| --- | --- | ---: | --- | --- |
| 2026-06-13 | STR-011 | no score movement | `ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_compositor|pp_paint_renderer_stroke_execution" --output-on-failure`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | `fa6ac4dc` |
### STR-010 - Extract Remaining Draw Merge Composite Orchestration
### STR-012 - Extract Preview Final Composite Orchestration
Status: Ready
Score: no score movement
Debt: `DEBT-0036`
Scope: `src/node_stroke_preview.cpp`, `tests/paint_renderer/compositor_tests.cpp`
Goal:
Extract the remaining preview final-composite and copy-back glue so the
preview path keeps only concrete texture binding and pass ordering.
Done Checks:
- `NodeStrokePreview::draw_stroke_immediate()` no longer owns the final
composite and preview copy-back orchestration inline.
- Regression coverage proves final composite and preview copy order.
- `DEBT-0036` records the reduced preview final-pass surface.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_compositor|pp_paint_renderer_stroke_execution" --output-on-failure
cmake --build --preset windows-msvc-default --config Debug --target PanoPainter
```
Status: Done
Score: +2 renderer boundary and OpenGL parity
Debt: `DEBT-0036`
Scope: `src/canvas.cpp`, `src/legacy_canvas_draw_merge_services.*`,
`tests/paint_renderer/compositor_tests.cpp`
Goal:
Move the remaining inline `Canvas::draw_merge()` branch orchestration into
retained helpers so the merge path keeps only concrete framebuffer, sampler,
and texture wiring. Preserve per-plane order, temporary-stroke behavior, and
final merge composition.
Done Checks:
- `Canvas::draw_merge()` no longer contains the remaining large inline branch
orchestration for temporary-stroke or blend/final-plane composition.
- Regression coverage proves the extracted helper preserves ordering and
branch behavior.
- `docs/modernization/debt.md` records the reduced draw-merge callback surface.
Validation:
```powershell
ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_compositor" --output-on-failure
& 'C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe' out\build\windows-msvc-default\tests\pp_paint_renderer_compositor_tests.vcxproj /p:Configuration=Debug /p:Platform=x64
```
Completed Task Log:
| Date | Task | Score | Validation | Commit |
| --- | --- | ---: | --- | --- |
| 2026-06-13 | STR-010 | +2 renderer boundary and OpenGL parity | `ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_compositor|pp_paint_renderer_stroke_execution" --output-on-failure`; `& 'C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe' out\build\windows-msvc-default\PanoPainter.vcxproj /p:Configuration=Debug /p:Platform=x64` | `pending` |