Files
manual_slop/conductor/tracks/default_layout_extract_20260629/spec.md
T
ed 410d81fb3f fix(track): correct line numbers in default_layout_extract spec/plan for master (not cruft branch)
The spec was drafted while the working tree was on tier2/post_module_taxonomy_de_cruft_20260627, but the track targets master. 2 line numbers were from the cruft branch, not master:
- src/commands.py reset_layout: spec said :342-378 + :371; master is :248-275 + :268
- src/command_palette.py: spec said 208 lines; master is 165 lines

Also added a Branch State Warning section documenting:
- main working tree is on tier2/post_module_taxonomy_de_cruft_20260627 (NOT master)
- module_taxonomy_refactor_20260627 + post_module_taxonomy_de_cruft_20260627 are NOT merged to master
- this track does NOT depend on those cruft tracks
- master worktree at C:\projects\manual_slop_master is the editing surface

All other line numbers (App._post_init:566, App.run:619, _run_immapp_result:691, _post_init_callback_result:1449, render_persona_editor_window:3433, orphan end_child:6990, paths.py themes:60/83/150/209-216/295) verified correct against master.
2026-06-29 22:18:25 -04:00

25 KiB

Track Specification: Default Layout Extract + Hard Visual Verification

Overview

Extract tier-2's GOOD work on the default layout setup (the layouts/ directory, the install-on-empty-INI helpers, the pre-run install timing fix, and the orphan-end-child cleanup) into master, and replace the previous tier-2 "fake" verification (INI content assertions only) with a HARD 4-layer visual verification protocol that catches the "panels don't render" regression every time it occurs.

Current State Audit (as of commit 466d2656 on master)

Branch State Warning

The main working tree at C:\projects\manual_slop is currently on branch tier2/post_module_taxonomy_de_cruft_20260627 (NOT master). This track targets master. All line numbers below are from master (verified via git show master:src/gui_2.py). The cruft-elimination tracks (module_taxonomy_refactor_20260627 + post_module_taxonomy_de_cruft_20260627) are NOT merged to master — they live on tier-2 branches only. This track does NOT depend on those cruft tracks; it depends only on cruft_elimination_20260627 (which IS merged to master) + the themes infrastructure in src/paths.py (which is on master). A separate master worktree exists at C:\projects\manual_slop_master for editing on the master branch without disturbing the cruft-branch working tree.

Already Implemented on Master

  • src/paths.py:60,83,150,209-216 — themes infrastructure (the pattern to mirror for layouts): themes: Path field in _AppPaths, default root_dir / "themes", env override SLOP_GLOBAL_THEMES, getters get_global_themes_path() and get_project_themes_path(project_root), plus the path info dict entry at line 295.
  • src/theme_2.py:340-346 + src/theme_models.py:181-225 — themes loader pair (the pattern to mirror for layouts): load_themes_from_disk() calls get_global_themes_path() then load_themes_from_dir(path, scope); the latter iterates children, parses, builds typed @dataclass(frozen=True, slots=True) records, drains errors via Result + ErrorInfo.
  • src/gui_2.py:1776from src.command_palette import render_palette_modal. MASTER WORKS: src/command_palette.py EXISTS (165 lines, has Command, ScoredCommand, CommandRegistry, render_palette_modal). Tier-2 broke because they deleted src/command_palette.py in module_taxonomy_refactor_20260627 (commit 3dd153f7, NOT merged to master).
  • src/gui_2.py:580-611_diag_layout_state (one-shot startup diagnostic that logs show_windows count + INI file size + stale window name warnings). Used as the install verification hook.
  • src/gui_2.py:619-703App.run. Calls _run_immapp_result(self) at line 691. HelloImGui reads runner_params.ini_filename ("manualslop_layout.ini") from cwd at load_user_pref time, BEFORE callbacks.post_init fires.
  • src/gui_2.py:566-578App._post_init. Calls _post_init_callback_result and _diag_layout_state. Fires AFTER HelloImGui has loaded the INI from disk.
  • src/gui_2.py:1449-1470_post_init_callback_result (drain-aware wrapper for App._post_init). The pattern Tier-2's _install_default_layout_if_empty_result and _install_default_layout_pre_run_result follow.
  • src/gui_2.py:1658-1660 — orphan-end-child bug was refactored OUT of _tier_stream_scroll_sync_result (the helper that was previously buggy). The orphan at line 6990 (in render_tier_stream_panel's except block) STILL exists on master.
  • src/gui_2.py:6981-6991render_tier_stream_panel has the latent orphan-end-child bug: try: ... imgui.end_child() at line 6988; except (TypeError, AttributeError): imgui.end_child() at line 6990. When the try block raises (e.g. len(None)), the second end_child() fires with no matching begin_child() and ImGui emits "In window 'MainDockSpace': Missing End()". Currently latent because len(content) rarely raises.
  • tests/conftest.py:700-712 — pre-baked tests/artifacts/manualslop_layout_default.ini shipped to fresh test workspaces. Hardcoded path (cwd-relative test fixture) — violates "production code uses cwd-relative paths only" rule.
  • src/commands.py:248-275reset_layout command with hardcoded tests/artifacts/live_gui_workspace/manualslop_layout.ini path at line 268 (dead code in production; references a test-fixture path that doesn't exist in production cwd).
  • conductor/tracks/default_layout_install_20260629/ — Tier-1 track scaffolding from this session. States the user's intent.
  • conductor/tracks/default_layout_install_followup_20260629/ — Tier-1 followup track that supersedes Tier-2's wrong-theory e9654518 strip-docking fix.

Already Implemented on Tier-2 Branch (NOT on master)

  • layouts/default.ini (2971 bytes, 101 lines) — bundled INI with full [Docking][Data] hierarchy (DockSpace ID=0xAFC85805 + DockNode 0x00000001 + DockNode 0x00000002 + 8 per-window DockId=... entries). Comments document the runtime-generated ID semantics.
  • src/layouts.py (3178 bytes, 88 lines) — LayoutFile dataclass + load_layouts_from_file() + load_layouts_from_dir() + load_layouts_from_disk() (mirrors src/theme_models.py:181-225 shape exactly).
  • src/gui_2.py:1481-1540_install_default_layout_if_empty + _install_default_layout_if_empty_result (drain-aware wrapper). The function: reads dst INI; if empty (<1000 bytes OR no [Window][), reads bundled src INI, writes to dst, calls imgui.load_ini_settings_from_memory(src_text) to apply to live session.
  • src/gui_2.py:1543-1590_install_default_layout_pre_run_result. Same logic but disk-only (no load_ini_settings_from_memory) because imgui is not yet initialized before immapp.run(). This is the timing fix Tier-2 added after the post-init version was too late for the first session.
  • src/gui_2.py:701-706App.run wiring: calls _install_default_layout_pre_run_result(self) BEFORE _run_immapp_result(self). Drains errors to _startup_timeline_errors.
  • src/gui_2.py:579-582App._post_init wiring: calls _install_default_layout_if_empty_result(self, src_layout_path, dst_layout_path). Drains errors.
  • tests/test_layout_reorganization.py (66 lines) — RED tests for the install-on-empty-INI behavior (per tier-2 claim "17/17 PASSED"; tests check INI content, not visible panels).

Gaps to Fill (This Track's Scope)

Gap Severity Layer
layouts/ directory + layouts/default.ini + src/layouts.py missing on master High (the assets themselves)
_install_default_layout_if_empty + _install_default_layout_pre_run_result helpers missing on master High (the install behavior)
App._post_init and App.run wiring missing on master High (the install triggers)
get_layouts_dir() in src/paths.py missing on master High (the path resolver; mirrors themes)
reset_layout command still references dead tests/artifacts/manualslop_layout_default.ini path Medium cleanup
Orphan imgui.end_child() at src/gui_2.py:6990 (latent; fires when tier-stream try-block raises) Medium cleanup
No hard verification that panels actually render visually Critical verification infrastructure

Tier-2's "Bullshit" We're NOT Extracting

Commit Why Skip
e9654518 "strip stale dockspace IDs" Wrong theory (superseded by 2afb0126; that one we DO extract)
13ad9d3e "idk" Meaningless commit message; bulk-edited manualslop_layout.ini
28527851 "artifacts" Meaningless commit; bulk-edited artifacts
9437af6c "archive 27 diagnostic scripts" 27 throwaway scripts not needed in master
4acf8b15, b80e5afb, c42a7599, cf5244b1, b1632f46, 06476c56, 519e1340, cf6a2e20, 4bf5ecd6, 5e53d477, d4116f19, 7d5a5492, 15cd1262, 23566da8 Tier-2 internal track-marking commits; we write our own
71028dad "drop stale from src.command_palette import" Tier-2 specific: master has src/command_palette.py so the import WORKS on master. The stale import bug only exists on tier-2 because they deleted the module. We do not cherry-pick this.

Why the User Wants This Track

The tier-2 track was marked "SHIPPED" based on:

  • 17/17 install/layout tests PASS (which only check INI content, not visible panels)
  • Manual launch produces a 3072-byte INI with correct structure (content check, not visible check)
  • "the imgui core loader rejected the literal IDs from the bundled INI because the runtime IDs didn't match" — claim contradicted by post-fix INI matching runtime IDs

None of those commits empirically verified visible panels after install. The user wants this regression to never happen again. The previous tier-2 "fake" verification must be replaced by a HARD one.

Goals

G1. Master has layouts/default.ini + src/layouts.py + get_layouts_dir() so the app boots with a non-empty INI on first launch.

G2. Master has _install_default_layout_if_empty + _install_default_layout_pre_run_result wired into App._post_init + App.run so empty-INI detection + install-on-empty works at both phases (live session + first session).

G3. Master has reset_layout cleaned up to remove the dead test-fixture path (no more tests/artifacts/... in production code).

G4. Master has the orphan imgui.end_child() at src/gui_2.py:6990 removed.

G5. Master has a HARD 4-layer visual verification infrastructure:

  • Layer 1 (Per-Panel Sentinel): a tests/test_panels_visible_after_install.py test that asserts every show_windows[k]==True panel has nonzero render size after first frame.
  • Layer 2 (Win32 PrintWindow Pixel Baseline): a tests/test_visual_baseline_default.py test that captures the running GUI window's pixels via Win32 PrintWindow API and compares against tests/artifacts/visual_baseline_default.png with <1% pixel-diff tolerance. Catches ALL visual regressions (empty workspace, wrong INI, missing panels, overlap, theme corruption).
  • Layer 3 (Forced Test Viewport + Theme): MANUAL_SLOP_TEST_VIEWPORT=1680x1050 + MANUAL_SLOP_TEST_THEME=dark env vars honored at startup. Forces fixed viewport + known theme so the baseline PNG is deterministic.
  • Layer 4 (Cannot-Skip Gates): scripts/check_visual_baseline.py (exits 1 if pixel diff > 1%); wire into scripts/run_tests_batched.py; require git tag VERIFIED-<YYYYMMDD> on the merge commit; conductor/tracks.md schema update so [x]-completion requires the tag.

G6. A regression test demonstrates that the verification infrastructure catches the original "panels don't render" bug (negative test: corrupt the installed INI, verify the sentinel + pixel baseline both fail).

Functional Requirements

FR1. Tier-2 Asset Extraction (Hybrid Approach C)

  • F1.1. Port layouts/default.ini fresh from tier-2's C:\projects\manual_slop_tier2\layouts\default.ini (2971 bytes, 101 lines) to layouts/default.ini at master repo root. Rationale: clean history for new asset; user-facing content.
  • F1.2. Port src/layouts.py fresh from tier-2's C:\projects\manual_slop_tier2\src\layouts.py (88 lines). Mirrors src/theme_models.py:181-225 shape. Rationale: clean history for new module; matches src/theme_2.py + src/theme_models.py pair.
  • F1.3. Add get_layouts_dir() to src/paths.py mirroring get_global_themes_path() at line 209. Add layouts: Path field to _AppPaths (line 60), default root_dir / "layouts" (line 83), env override SLOP_GLOBAL_LAYOUTS (line 150), path info dict entry (line 295). User explicitly authorized "make a layouts directory similar to the themes directory" in the prior session.
  • F1.4. Port tests/test_layout_reorganization.py fresh from tier-2 (66 lines). Rationale: tests for the install helpers.

FR2. Install Helpers + Wiring

  • F2.1. Add _install_default_layout_if_empty(src_ini: Path, dst_ini: Path) -> Result[bool] to src/gui_2.py (per tier-2 line 1481). Reads dst; if empty (<1000 bytes OR no [Window][), copies src→dst and calls imgui.load_ini_settings_from_memory(src_text) to apply to live session.
  • F2.2. Add _install_default_layout_if_empty_result(app: "App", src: Path, dst: Path) -> Result[bool] (per tier-2 line 1530). Drain-aware passthrough wrapper.
  • F2.3. Add _install_default_layout_pre_run_result(app: "App") -> Result[bool] (per tier-2 line 1543). Disk-only install (no load_ini_settings_from_memory); imgui isn't initialized yet.
  • F2.4. Wire _install_default_layout_if_empty_result into App._post_init (line 566-578). Source path: get_layouts_dir() / "default.ini". Dst path: Path.cwd() / "manualslop_layout.ini". Drain errors to _startup_timeline_errors.
  • F2.5. Wire _install_default_layout_pre_run_result into App.run (line 619-703, insert before line 691 _run_immapp_result(self)). Drain errors to _startup_timeline_errors.

FR3. Surgical Cherry-Picks

  • F3.1. Cherry-pick c2155593 fix(gui): remove orphan imgui.end_child() in render_tier_stream_panel except handler. Apply the 1-line deletion to src/gui_2.py:6990. Tier-2 verified this fixes an imgui "Missing End()" error in MainDockSpace when the tier-stream try-block raises. Latent on master but real.
  • F3.2. Cherry-pick 3b966288 chore(commands): remove dead test-fixture path from reset_layout. Apply the deletion to src/commands.py:268 (the tests/artifacts/live_gui_workspace/manualslop_layout.ini hardcoded path in the layout_paths list).

FR4. Layer 1 — Per-Panel Render Sentinel

  • F4.1. New test file tests/test_panels_visible_after_install.py. Imports live_gui fixture from tests/conftest.py.
  • F4.2. RED: assert that for each show_windows[k]==True entry, after first frame, imgui.find_window_viewport(k).size.x > 0 AND .size.y > 0. Test should fail on the current baseline (we don't have the install helpers yet) — confirms sentinel catches the regression.
  • F4.3. GREEN: with the install helpers in place (FR2), test passes.
  • F4.4. Test must use poll-loop (not time.sleep) per conductor/workflow.md "Async Setters Need Poll-For-State".

FR5. Layer 2 — Win32 PrintWindow Pixel Baseline

  • F5.1. New test file tests/test_visual_baseline_default.py. Imports live_gui fixture.
  • F5.2. Capture: import win32gui from pywin32; find imgui window HWND via win32gui.FindWindow(None, "manual slop"); allocate DC + bitmap; call win32gui.PrintWindow(hwnd, hdc, PW_RENDERFULLCONTENT); convert bitmap to PNG via Pillow (already a dep); save to tests/artifacts/<test_session>_<date>.png.
  • F5.3. Baseline: commit tests/artifacts/visual_baseline_default.png (the "known good" reference). Generated AFTER F5.1 + F5.2 are GREEN against the new install infrastructure.
  • F5.4. Compare: load baseline + current via Pillow.Image.open(...); convert to RGB; compute pixel diff via numpy.abs(np.array(a) - np.array(b)).mean() / 255.0. Threshold: 0.01 (1%). Fail if > 1%.
  • F5.5. RED: with the install infrastructure removed, the test must fail. Confirms the test catches the regression.
  • F5.6. Test must poll for first frame + capture screenshot AT MOST ONCE (don't spam captures).

FR6. Layer 3 — Forced Test Viewport + Theme

  • F6.1. Add MANUAL_SLOP_TEST_VIEWPORT=1680x1050 env var support to App.run (line 619). If set, override self.runner_params.app_window_params.window_geometry.size to the env-var value (parsed as WxH).
  • F6.2. Add MANUAL_SLOP_TEST_THEME=dark env var support to App.run (line 619). If set, force self.runner_params.imgui_window_params.tweaked_theme = ImGuiTheme_.ImGuiColorsDark (the default dark theme).
  • F6.3. RED: write tests/test_test_mode_env_vars.py that asserts both env vars are honored when set (via live_gui fixture with env vars).
  • F6.4. GREEN: implement the env-var parsing in App.run.

FR7. Layer 4 — Cannot-Skip Gates

  • F7.1. New file scripts/check_visual_baseline.py. Imports live_gui (no — too heavy for a CLI script). Instead, accepts --baseline <path> + --current <path> + --threshold <float> CLI args. Uses Pillow.Image.open() + numpy.abs(...).mean() to compute diff. Exits 1 if diff > threshold.
  • F7.2. Add scripts/check_visual_baseline.py to scripts/run_tests_batched.py tier-2 test list (or a new tier dedicated to visual regression).
  • F7.3. Document the VERIFIED-<YYYYMMDD> git-tag requirement in conductor/tracks.md schema section. Tracks that touch src/gui_2.py MUST carry the tag for [x]-completion.
  • F7.4. New doc docs/guide_visual_verification.md (200-300 lines). Documents the 4 layers, how to add a new visual baseline, how to update an existing baseline, the env-var protocol, the tag protocol.

FR8. Negative Test (Regression Catch Demonstration)

  • F8.1. New test file tests/test_visual_baseline_catches_corrupt_ini.py. Uses live_gui fixture; AFTER the install infrastructure has run, manually corrupt the installed INI (delete [Docking][Data] line). Re-launch + capture screenshot. Verify pixel diff > 5% (the corrupted INI shows empty workspace, baseline shows full panels).
  • F8.2. Negative test must run in a separate pytest session (not pollute live_gui state).

Non-Functional Requirements

NFR1. Atomic Per-Task Commits

Every Phase task results in exactly ONE atomic commit. No batched commits. Per AGENTS.md "Critical Anti-Patterns" — "Do not batch commits - commit per-task for atomic rollback".

NFR2. TDD Red-First

Every implementation task has a preceding RED test task. Per conductor/workflow.md "Standard Task Workflow" §4.

NFR3. No Comments in Source Code

Per AGENTS.md "Critical Anti-Patterns" — "Do not add comments to source code; documentation lives in /docs".

NFR4. No Diagnostic Noise in Production

Per AGENTS.md "Critical Anti-Patterns" — diag stderr goes to tests/artifacts/*.diag.log or /tmp, NOT src/*.py.

NFR5. 1-Space Indentation

Per conductor/workflow.md "Code Style (MANDATORY - Python)" — exactly 1 space per level for ALL Python code.

NFR6. CRLF Line Endings on Windows

Per conductor/workflow.md "Code Style (MANDATORY - Python)" — preserve CRLF.

NFR7. Type Hints Required

Per conductor/product-guidelines.md "AI-Optimized Compact Style" — strict type hints on all parameters, return types, globals.

NFR8. No dict[str, Any] / Optional[T] in Non-Boundary Code

Per conductor/code_styleguides/data_oriented_design.md §8.5 + python.md §17. Typed @dataclass(frozen=True, slots=True) + Result[T] + NIL_T.

NFR9. ImGui Defer Patterns

Per conductor/code_styleguides/python.md — use imscope context managers over manual imgui.begin/end pairs (where applicable). Existing manual pairs in src/gui_2.py are unchanged.

NFR10. Manual Slop MCP Tools Only

Per the system prompt — use manual-slop_* MCP tools, NOT native read/edit/grep (where the MCP equivalents are available). When MCP tools aren't available (which is the case for this Tier-1 track creation), native read/edit/grep/write are the fallback.

Architecture Reference

  • docs/guide_gui_2.md §"App class lifecycle" + §"_post_init + App.run" — current rendering flow; where the install helpers slot in.
  • docs/guide_architecture.md §"Thread domains, event system" — confirms main thread owns App.run; install helpers run on main thread (no thread-safety concerns).
  • docs/guide_testing.md §"live_gui fixture" + §"Puppeteer pattern" + §"Structural Testing Contract" — the live_gui fixture is the test harness for FR4-FR8.
  • conductor/code_styleguides/data_oriented_design.md §8.5 — the Python Type Promotion Mandate. Bound by NFR8.
  • conductor/code_styleguides/error_handling.mdResult[T] + ErrorInfo + ErrorKind usage. The install helpers return Result[bool] per this styleguide.
  • conductor/code_styleguides/type_aliases.mdMetadata = TrackMetadata etc. The new LayoutFile dataclass follows the typed-record pattern from this styleguide.
  • conductor/code_styleguides/feature_flags.md — "delete to turn off" (file presence) for the bundled INI. If layouts/default.ini is deleted, _install_default_layout_if_empty returns Result(data=False) (no install).
  • docs/guide_visual_verification.md (NEW, FR7.4) — the documentation deliverable.

Out of Scope

  1. Fleury declarative view-constructs migration (PANELS: tuple[PanelDef, ...]). Logged in default_layout_install_20260629/metadata.json deferred_to_followup_tracks[0]. Requires its own track.
  2. imgui_test_engine integration (test_engine_integration_20260627). Provides pixel-level diff via ctx.capture_screenshot_window(). Our Win32 PrintWindow approach is simpler + works without test engine. The two approaches are complementary; layering them is a future task.
  3. Reverting tier-2's working tree state. User's responsibility per the Inherited-Cruft rule. Tier-2's git status shows uncommitted manual_slop.toml + manual_slop_history.toml deletions; user must explicitly handle those.
  4. Cross-platform pixel diff (Linux/macOS). Win32 PrintWindow is Windows-only. The track ships Windows-only; CI on Linux/macOS would skip FR5 (marked @pytest.mark.skipif(sys.platform != "win32")).
  5. Pre-baked test INI shipped from tests/conftest.py:700-712. Replaced by FR5.3 baseline PNG.
  6. render_persona_editor_window bug at src/gui_2.py:3433+ (opens + immediately closes the Persona Editor window when not embedded). Pre-existing; unrelated to panel visibility. Logged for followup.

Coordination with Pending Tracks

  • default_layout_install_20260629/ — supersedes. Tier-1 scaffolding for this work. The plan.md tasks here replace conductor/tracks/default_layout_install_20260629/plan.md.
  • default_layout_install_followup_20260629/ — supersedes. The followup plan assumed tier-2's e9654518 INI strip was the right fix; this track's plan supersedes that with the hybrid extraction.
  • test_engine_integration_20260627 — independent. Not blocked by, does not block this track. May consume the env-var protocol (FR6.1 + F6.2) once integrated.
  • panel_defs_fleury_migration_20260629 (deferred) — future. Will consume LayoutFile + get_layouts_dir() from this track.

Verification Criteria (Track Completion Gates)

  • All Phase 1-9 tasks committed (atomic per-task)
  • tests/test_panels_visible_after_install.py passes (Layer 1 sentinel)
  • tests/test_visual_baseline_default.py passes (Layer 2 pixel diff < 1%)
  • tests/test_test_mode_env_vars.py passes (Layer 3 env vars honored)
  • tests/test_visual_baseline_catches_corrupt_ini.py passes (FR8 negative test)
  • scripts/check_visual_baseline.py --help works; --strict mode exits 1 on diff > 1%
  • scripts/run_tests_batched.py includes the visual verification tests
  • tests/artifacts/visual_baseline_default.png is committed to master
  • docs/guide_visual_verification.md is committed; cross-referenced from docs/Readme.md
  • conductor/tracks.md schema updated to require VERIFIED-<YYYYMMDD> tag for [x]-completion of tracks touching src/gui_2.py
  • MANUAL GATE: user runs uv run sloppy.py from master, confirms panels render visibly. User commits the VERIFIED-<date> tag.
  • docs/reports/TRACK_COMPLETION_default_layout_extract_20260629.md committed
  • Tier-2 branch status: marked for archival (user's responsibility per AGENTS.md "Inherited-Cruft")

Scope Summary (per workflow.md "Tier 1 Track Initialization Rules")

  • Scope: 9 phases, ~36 tasks
  • Files touched: ~12 (3 new: src/layouts.py, layouts/default.ini, tests/artifacts/visual_baseline_default.png, scripts/check_visual_baseline.py, docs/guide_visual_verification.md; 6 modified: src/gui_2.py, src/paths.py, src/commands.py, tests/test_layout_reorganization.py, tests/test_panels_visible_after_install.py (new), tests/test_visual_baseline_default.py (new), tests/test_test_mode_env_vars.py (new), tests/test_visual_baseline_catches_corrupt_ini.py (new), scripts/run_tests_batched.py, conductor/tracks.md, docs/Readme.md)
  • Sites modified: ~15 (in _post_init, App.run, _install_default_layout_*, _diag_layout_state, etc.)
  • Tasks: ~36

Risk Register

  • R1 — Win32 PrintWindow may fail for the imgui-bundle HelloImGui window (HWND lookup or print flags). Mitigation: pre-flight check win32gui.IsWindow(hwnd) before capture; fall back to BitBlt of the screen region.
  • R2 — Pixel baseline may be too sensitive (font hinting, GPU driver variations). Mitigation: tolerance is 1%; if false positives appear, raise to 2% and document.
  • R3 — Forced viewport env var may not work on multi-monitor systems. Mitigation: scope the env var to test fixtures only (tests/conftest.py sets it before spawning).
  • R4 — Tier-2 sandbox has uncommitted edits that may conflict when cherry-picking. Mitigation: cherry-pick to master directly (master is clean); tier-2 archival is user's responsibility.
  • R5 — User-visible panel rendering depends on _install_default_layout_pre_run_result firing BEFORE immapp.run. If the user's cwd already has a valid manualslop_layout.ini, the install is skipped. The pixel baseline test must run with cwd-deleted manualslop_layout.ini to exercise the install path. Mitigation: live_gui fixture already cleans cwd before spawning.