Merge branch 'master' into tier2/default_layout_install_20260629
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
{
|
||||
"track_id": "default_layout_extract_20260629",
|
||||
"name": "Default Layout Extract + Hard Visual Verification",
|
||||
"status": "active",
|
||||
"created_date": "2026-06-29",
|
||||
"summary": "Extract tier-2's GOOD default-layout work (layouts/, src/layouts.py, install helpers, orphan-end-child fix, reset_layout cleanup) into master via hybrid porting + cherry-pick. Build 4-layer visual verification infrastructure (per-panel sentinel + Win32 PrintWindow pixel baseline + forced viewport/theme env vars + cannot-skip tags) that catches 'panels don't render' regressions every time they occur.",
|
||||
"estimated_effort": {
|
||||
"method": "scope (per workflow.md §Tier 1 Track Initialization Rules). NO day estimates.",
|
||||
"scope": "9 phases, 36 tasks. 3 new files (src/layouts.py, layouts/default.ini, scripts/check_visual_baseline.py, docs/guide_visual_verification.md, tests/artifacts/visual_baseline_default.png). 6 modified files (src/gui_2.py, src/paths.py, src/commands.py, scripts/run_tests_batched.py, conductor/tracks.md, docs/Readme.md). 9 new test files (RED tests for each helper + 3 negative tests). ~36 atomic commits.",
|
||||
"phase_1": "6 tasks: foundational assets (layouts/, src/layouts.py, get_layouts_dir)",
|
||||
"phase_2": "4 tasks: install helpers (_install_default_layout_if_empty + pre_run)",
|
||||
"phase_3": "5 tasks: wiring (App._post_init + App.run)",
|
||||
"phase_4": "2 tasks: surgical cherry-picks (c2155593 + 3b966288)",
|
||||
"phase_5": "3 tasks: Layer 1 sentinel",
|
||||
"phase_6": "5 tasks: Layer 2 pixel baseline",
|
||||
"phase_7": "4 tasks: Layer 3 forced viewport/theme",
|
||||
"phase_8": "5 tasks: Layer 4 cannot-skip gates",
|
||||
"phase_9": "7 tasks: negative test + verification + track completion"
|
||||
},
|
||||
"scope": {
|
||||
"new_files": [
|
||||
"src/layouts.py",
|
||||
"layouts/default.ini",
|
||||
"scripts/check_visual_baseline.py",
|
||||
"docs/guide_visual_verification.md",
|
||||
"tests/artifacts/visual_baseline_default.png",
|
||||
"tests/test_layouts.py",
|
||||
"tests/test_paths_layouts.py",
|
||||
"tests/test_layouts_bundled.py",
|
||||
"tests/test_install_default_layout.py",
|
||||
"tests/test_app_wiring_install.py",
|
||||
"tests/test_panels_visible_after_install.py",
|
||||
"tests/test_visual_baseline_default.py",
|
||||
"tests/test_test_mode_env_vars.py",
|
||||
"tests/test_visual_baseline_catches_corrupt_ini.py"
|
||||
],
|
||||
"modified_files": [
|
||||
"src/gui_2.py",
|
||||
"src/paths.py",
|
||||
"src/commands.py",
|
||||
"scripts/run_tests_batched.py",
|
||||
"conductor/tracks.md",
|
||||
"docs/Readme.md"
|
||||
],
|
||||
"deleted_files": []
|
||||
},
|
||||
"goals": [
|
||||
"G1. Master has layouts/ + src/layouts.py + get_layouts_dir() so app boots with non-empty INI on first launch",
|
||||
"G2. Master has _install_default_layout_* helpers wired into App._post_init + App.run so empty-INI install works at both phases",
|
||||
"G3. Master has reset_layout cleaned up to remove dead test-fixture path",
|
||||
"G4. Master has orphan imgui.end_child() at src/gui_2.py:6990 removed",
|
||||
"G5. Master has HARD 4-layer visual verification infrastructure (sentinel + pixel baseline + forced viewport/theme + cannot-skip gates)",
|
||||
"G6. A regression test demonstrates the verification catches the original 'panels don't render' bug"
|
||||
],
|
||||
"verification_criteria": [
|
||||
"All Phase 1-9 tasks committed (atomic per-task, ~36 commits)",
|
||||
"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)"
|
||||
],
|
||||
"blocked_by": {
|
||||
"default_layout_install_20260629": "superseded (this track replaces it)"
|
||||
},
|
||||
"blocks": {
|
||||
"panel_defs_fleury_migration": "future (consumes LayoutFile + get_layouts_dir from this track)"
|
||||
},
|
||||
"tier_2_specific_commits_to_skip": {
|
||||
"rationale": "Tier-2 branch is 143 commits ahead of master. Only 8 commits are the default-layout work. The rest (RAG fixes, MMA stress tests, module taxonomy refactors) are NOT relevant to this track. Specific tier-2 commits NOT to extract:",
|
||||
"skip_list": [
|
||||
"e9654518 (wrong-theory INI strip — superseded by 2afb0126 which we DO extract)",
|
||||
"13ad9d3e (commit message 'idk' — meaningless)",
|
||||
"28527851 (commit message 'artifacts' — meaningless)",
|
||||
"9437af6c (27 diagnostic scripts — noise)",
|
||||
"4acf8b15, b80e5afb, c42a7599, cf5244b1, b1632f46, 06476c56, 519e1340, cf6a2e20, 4bf5ecd6, 5e53d477, d4116f19, 7d5a5492 (tier-2 internal track-marking commits)",
|
||||
"71028dad (drop stale from src.command_palette import — tier-2 specific; master has src/command_palette.py so the import WORKS on master; do NOT cherry-pick)"
|
||||
],
|
||||
"extract_list": [
|
||||
"7577d7d2 (chore: introduce layouts/ + src/layouts.py) — port fresh via FR1.1 + FR1.2",
|
||||
"f3cd7bc2 (feat: install-on-empty-INI helpers) — port fresh via FR2.1 + FR2.2",
|
||||
"3d87f8e7 (fix: wire into App._post_init) — port fresh via FR2.4",
|
||||
"3b966288 (chore: remove dead test-fixture path) — cherry-pick via FR3.2",
|
||||
"2afb0126 (fix: restore [Docking] structure) — port fresh via FR1.1",
|
||||
"79c25a32 (fix: pre-run install timing) — port fresh via FR2.3 + FR2.5",
|
||||
"71028dad SKIPPED (master has src/command_palette.py)",
|
||||
"c2155593 (fix: remove orphan imgui.end_child) — cherry-pick via FR3.1"
|
||||
]
|
||||
},
|
||||
"regressions_and_pre_existing_failures": [],
|
||||
"pre_existing_failures_remaining": [],
|
||||
"deferred_to_followup_tracks": [
|
||||
{
|
||||
"title": "panel_defs_fleury_migration",
|
||||
"description": "Migrate src/gui_2.py render_*_window functions to Ryan Fleury's declarative view-constructs pattern. PANELS: tuple[PanelDef, ...]. Per docs/transcripts/rcJwvx2CTZY_ryan_fleury_raddbg_codebase_intro.json v1@2237s and docs/transcripts/_9_bK_WjuYY_ryan_fleury_raddbg_walkthrough.json v2@7697s.",
|
||||
"track_status": "deferred",
|
||||
"depends_on_this_track": ["src/layouts.py", "LayoutFile", "get_layouts_dir"]
|
||||
},
|
||||
{
|
||||
"title": "render_persona_editor_window empty-content bug fix",
|
||||
"description": "src/gui_2.py:3433+ opens + immediately closes the Persona Editor window when not embedded. Pre-existing bug, unrelated to panel visibility. Will be discovered via Layer 1 sentinel (panel renders but content is empty).",
|
||||
"track_status": "deferred",
|
||||
"depends_on_this_track": ["Layer 1 per-panel sentinel"]
|
||||
},
|
||||
{
|
||||
"title": "test_engine_integration_20260627",
|
||||
"description": "imgui-bundle test engine integration. Provides ctx.capture_screenshot_window() + pixel-level diff via imgui.test_engine. Our Win32 PrintWindow approach is simpler but Windows-only. The two approaches are complementary.",
|
||||
"track_status": "in_progress (separate track)"
|
||||
},
|
||||
{
|
||||
"title": "tier2_default_layout_install_20260629 archival",
|
||||
"description": "Tier-2 sandbox at C:\\projects\\manual_slop_tier2 has uncommitted edits (deleted manual_slop.toml + manual_slop_history.toml). User's responsibility per AGENTS.md Inherited-Cruft rule. Does NOT block this track.",
|
||||
"track_status": "user_action_required"
|
||||
}
|
||||
],
|
||||
"risk_register": [
|
||||
{
|
||||
"id": "R1",
|
||||
"description": "Win32 PrintWindow may fail for imgui-bundle HelloImGui window (HWND lookup or print flags)",
|
||||
"likelihood": "medium (the implementation is larger than the spec suggests)",
|
||||
"mitigation": "pre-flight check win32gui.IsWindow(hwnd) before capture; fall back to BitBlt of the screen region"
|
||||
},
|
||||
{
|
||||
"id": "R2",
|
||||
"description": "Pixel baseline may be too sensitive (font hinting, GPU driver variations)",
|
||||
"likelihood": "medium",
|
||||
"mitigation": "tolerance is 1%; if false positives appear, raise to 2% and document"
|
||||
},
|
||||
{
|
||||
"id": "R3",
|
||||
"description": "Forced viewport env var may not work on multi-monitor systems",
|
||||
"likelihood": "low",
|
||||
"mitigation": "scope the env var to test fixtures only (tests/conftest.py sets it before spawning)"
|
||||
},
|
||||
{
|
||||
"id": "R4",
|
||||
"description": "Tier-2 sandbox has uncommitted edits that may conflict when cherry-picking",
|
||||
"likelihood": "low (cherry-pick to master directly; master is clean)",
|
||||
"mitigation": "cherry-pick to master directly (master is clean); tier-2 archival is user's responsibility"
|
||||
},
|
||||
{
|
||||
"id": "R5",
|
||||
"description": "User-visible panel rendering depends on _install_default_layout_pre_run_result firing BEFORE immapp.run. If cwd already has a valid INI, install is skipped. The pixel baseline test must run with cwd-deleted manualslop_layout.ini to exercise the install path.",
|
||||
"likelihood": "low",
|
||||
"mitigation": "live_gui fixture already cleans cwd before spawning"
|
||||
}
|
||||
],
|
||||
"documentation_deliverables": [
|
||||
"conductor/tracks/default_layout_extract_20260629/spec.md",
|
||||
"conductor/tracks/default_layout_extract_20260629/plan.md",
|
||||
"conductor/tracks/default_layout_extract_20260629/metadata.json",
|
||||
"conductor/tracks/default_layout_extract_20260629/state.toml",
|
||||
"docs/guide_visual_verification.md (Layer 1-4 protocol)",
|
||||
"docs/reports/TRACK_COMPLETION_default_layout_extract_20260629.md (at end)"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,533 @@
|
||||
# Track Plan: Default Layout Extract + Hard Visual Verification
|
||||
|
||||
> **For Tier-3 workers:** Steps use checkbox (`- [ ]`) syntax. Use exactly **1-space indentation** for all Python. Preserve **CRLF** line endings. No comments in source code. Atomic commits per task. No `dict[str, Any]`, no `Optional[T]` returns (use `Result[T]` + `NIL_T`). Read `src/gui_2.py:1481-1540` (tier-2 version) for the install helper pattern reference; read `src/theme_models.py:181-225` for the layouts loader pattern reference; read `src/paths.py:60-83,150,209-216,295` for the themes → layouts mirror.
|
||||
|
||||
**Goal:** Extract tier-2's GOOD default-layout work into master AND build a hard 4-layer visual verification infrastructure that catches "panels don't render" regressions every time.
|
||||
|
||||
**Architecture:** Hybrid extraction (C per spec §FR1): port `layouts/default.ini` + `src/layouts.py` + `tests/test_layout_reorganization.py` fresh (clean history for new modules); cherry-pick `c2155593` (orphan end_child) + `3b966288` (reset_layout cleanup); add new `_install_default_layout_*` helpers + `App._post_init` + `App.run` wiring. Build 4 verification layers: per-panel render sentinel (Layer 1), Win32 PrintWindow pixel baseline (Layer 2), forced test viewport+theme env vars (Layer 3), cannot-skip gates (Layer 4: standalone CLI + CI integration + tag requirement + tracks.md schema).
|
||||
|
||||
**Tech Stack:** Python 3.11+, `imgui-bundle` (HelloImGui), `pywin32` (PrintWindow), `Pillow` (PNG), `numpy` (pixel diff), `pytest` + `live_gui` fixture. Adds `scripts/check_visual_baseline.py` (new audit-style script).
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Asset Foundation (layouts/ + src/layouts.py + get_layouts_dir)
|
||||
|
||||
Focus: Port the foundational assets from tier-2 to master with clean history.
|
||||
|
||||
- [ ] **Task 1.1: RED test for `src/layouts.py:load_layouts_from_dir`**
|
||||
- WHERE: New file `tests/test_layouts.py`
|
||||
- WHAT: Write 3 tests:
|
||||
1. `test_load_layouts_from_dir_empty` — pass a non-existent path → returns `{}`
|
||||
2. `test_load_layouts_from_dir_single_file` — create tmp dir with one `.ini` file → returns 1-entry dict keyed by stem
|
||||
3. `test_load_layouts_from_dir_skips_non_ini` — tmp dir with `.ini` + `.txt` → returns only the `.ini`
|
||||
- HOW: Use `tmp_path` fixture (already redirected under `tests/artifacts/_pytest_tmp` per `pyproject.toml:addopts`). Import `from src.layouts import load_layouts_from_dir`.
|
||||
- SAFETY: Use `tmp_path`, not hardcoded paths. 1-space indentation. Type hints required.
|
||||
- RUN: `uv run pytest tests/test_layouts.py -v` — Expected: `ModuleNotFoundError: No module named 'src.layouts'`.
|
||||
- COMMIT: `test(layouts): RED phase tests for load_layouts_from_dir`
|
||||
|
||||
- [ ] **Task 1.2: Create `src/layouts.py`**
|
||||
- WHERE: New file `src/layouts.py` (87 lines, ported fresh from tier-2's `C:\projects\manual_slop_tier2\src\layouts.py`)
|
||||
- WHAT: Define `LayoutFile` dataclass + `load_layouts_from_file()` + `load_layouts_from_dir()` + `load_layouts_from_disk()` + `_LAYOUTS_CACHE: dict[str, LayoutFile]`
|
||||
- HOW: Read tier-2 file; copy verbatim EXCEPT: strip the "TODO(Ed)" comment (NFR3); keep the `Result` + `ErrorInfo` drain pattern from tier-2 verbatim; keep `_LAYOUTS_CACHE` module-level
|
||||
- SAFETY: 1-space indentation. CRLF. `@dataclass(frozen=True, slots=True)`. Type hints on all params + returns.
|
||||
- RUN: `uv run pytest tests/test_layouts.py -v` — Expected: 3 PASS.
|
||||
- COMMIT: `feat(layouts): introduce src/layouts.py + LayoutFile dataclass`
|
||||
|
||||
- [ ] **Task 1.3: RED test for `src/paths.py:get_global_layouts_path`**
|
||||
- WHERE: New file `tests/test_paths_layouts.py`
|
||||
- WHAT: Write 4 tests:
|
||||
1. `test_get_global_layouts_path_default` — `initialize_paths()` called, `get_global_layouts_path()` returns `<root_dir>/layouts`
|
||||
2. `test_get_global_layouts_path_env_override` — `SLOP_GLOBAL_LAYOUTS` env var set → returns that path
|
||||
3. `test_layouts_in_path_info_dict` — `paths.path_info()` dict has `'layouts': info(...)` entry
|
||||
4. `test_layouts_field_in_app_paths` — `_AppPaths().layouts` is a `Path`
|
||||
- HOW: Import `from src.paths import get_global_layouts_path, initialize_paths, _cfg`. Use `monkeypatch.setenv("SLOP_GLOBAL_LAYOUTS", str(tmp_path / "custom"))`.
|
||||
- SAFETY: Call `initialize_paths()` once per test (use fixture). 1-space indentation.
|
||||
- RUN: `uv run pytest tests/test_paths_layouts.py -v` — Expected: `AttributeError: module 'src.paths' has no attribute 'get_global_layouts_path'`.
|
||||
- COMMIT: `test(paths): RED phase tests for get_global_layouts_path + SLOP_GLOBAL_LAYOUTS`
|
||||
|
||||
- [ ] **Task 1.4: Add `get_global_layouts_path()` to `src/paths.py`**
|
||||
- WHERE: `src/paths.py` — 4 sites: line 60 `_AppPaths` dataclass (add `layouts: Path`), line 83 `_PATHS_DEFAULTS` (add `layouts = root_dir / "layouts"`), line 150 `initialize_paths._resolve_path` chain (add `SLOP_GLOBAL_LAYOUTS` env override), line 295 `path_info()` (add `'layouts': info(cfg.layouts)`), line 209-216 (add `get_global_layouts_path()` mirror of `get_global_themes_path()`)
|
||||
- WHAT: Mirror the themes pattern exactly. New code follows the existing 1-space indentation + CRLF.
|
||||
- HOW: Read `src/paths.py:60` → insert `layouts: Path` after `themes: Path`. Read `src/paths.py:83` → insert `themes = root_dir / "layouts"` after `themes = root_dir / "themes"`. Read `src/paths.py:150` → add `themes = _resolve_path("SLOP_GLOBAL_LAYOUTS", "layouts", root_dir / "layouts", config_path)` to the resolver chain. Read `src/paths.py:209-216` → copy `get_global_themes_path()` verbatim and rename. Read `src/paths.py:295` → insert `'layouts': info(cfg.layouts)` after `'themes': info(cfg.themes)`.
|
||||
- SAFETY: Match existing 1-space indent. CRLF. No comments in source. Update `_resolve_path` keyword args to match the same shape as the themes line.
|
||||
- RUN: `uv run pytest tests/test_paths_layouts.py -v` — Expected: 4 PASS.
|
||||
- COMMIT: `feat(paths): add get_global_layouts_path() + SLOP_GLOBAL_LAYOUTS env override (mirror of themes)`
|
||||
|
||||
- [ ] **Task 1.5: RED test for bundled INI file**
|
||||
- WHERE: New file `tests/test_layouts_bundled.py`
|
||||
- WHAT: Write 4 tests:
|
||||
1. `test_layouts_default_ini_exists` — `Path("layouts/default.ini").exists()` is True
|
||||
2. `test_layouts_default_ini_size` — file size > 1000 bytes
|
||||
3. `test_layouts_default_ini_has_docking` — content contains `[Docking][Data]`
|
||||
4. `test_layouts_default_ini_has_8_windows` — content has 8 `[Window][X]` entries
|
||||
- HOW: Use `Path.cwd() / "layouts" / "default.ini"`. Use `len(re.findall(r"^\[Window\]\[", content))` for window count.
|
||||
- SAFETY: 1-space indent. CRLF. Read with `encoding="utf-8"`.
|
||||
- RUN: `uv run pytest tests/test_layouts_bundled.py -v` — Expected: `FileNotFoundError: layouts/default.ini`.
|
||||
- COMMIT: `test(layouts): RED phase tests for bundled default.ini structure`
|
||||
|
||||
- [ ] **Task 1.6: Port `layouts/default.ini` to master**
|
||||
- WHERE: New file `layouts/default.ini` at repo root
|
||||
- WHAT: Copy verbatim from tier-2's `C:\projects\manual_slop_tier2\layouts\default.ini` (2971 bytes, 101 lines). Strip the `;;;` documentation comments (NFR3: comments live in docs). Strip the `;;;<<<SplitIds>>>;;;` block at line 100-101 (HelloImGui adds that on save; not needed in the bundle).
|
||||
- HOW: Read tier-2 file → write fresh to `layouts/default.ini`. Keep all `[Window][X]` entries (8 of them), `[Docking][Data]` block with `DockSpace ID=0xAFC85805`, `[Layout]`, `[StatusBar]`, `[Theme]` sections.
|
||||
- SAFETY: CRLF. No `;;;` lines. Final file should be ~30-40 lines.
|
||||
- RUN: `uv run pytest tests/test_layouts_bundled.py -v` — Expected: 4 PASS.
|
||||
- COMMIT: `feat(layouts): bundle layouts/default.ini with 8 [Window] entries + [Docking] hierarchy`
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Install Helpers (RED-GREEN for the 3 helpers)
|
||||
|
||||
Focus: Add `_install_default_layout_if_empty`, `_install_default_layout_if_empty_result`, `_install_default_layout_pre_run_result` to `src/gui_2.py`.
|
||||
|
||||
- [ ] **Task 2.1: RED test for `_install_default_layout_if_empty` (empty dst)**
|
||||
- WHERE: New file `tests/test_install_default_layout.py`
|
||||
- WHAT: Write 5 tests:
|
||||
1. `test_install_empty_dst` — dst INI is empty/missing → src content copied to dst + `Result(data=True)`
|
||||
2. `test_install_skips_non_empty_dst` — dst INI has 5+ `[Window][` entries → no overwrite + `Result(data=False)`
|
||||
3. `test_install_handles_missing_src` — src INI doesn't exist → `Result(data=False, errors=[ErrorInfo])`
|
||||
4. `test_install_handles_oserror_on_read` — patch `Path.read_text` to raise OSError → `Result(data=False, errors=[ErrorInfo])`
|
||||
5. `test_install_calls_load_ini_settings_from_memory` — assert `imgui.load_ini_settings_from_memory` was called once
|
||||
- HOW: Use `tmp_path`. Import `from src.gui_2 import _install_default_layout_if_empty`. Use `monkeypatch.setattr(imgui, "load_ini_settings_from_memory", lambda x: None)` for test 5.
|
||||
- SAFETY: 1-space indent. CRLF. Mock only the boundary (`imgui.load_ini_settings_from_memory` is the SDK boundary).
|
||||
- RUN: `uv run pytest tests/test_install_default_layout.py -v` — Expected: `ImportError: cannot import name '_install_default_layout_if_empty'`.
|
||||
- COMMIT: `test(install): RED phase tests for _install_default_layout_if_empty`
|
||||
|
||||
- [ ] **Task 2.2: Implement `_install_default_layout_if_empty` in `src/gui_2.py`**
|
||||
- WHERE: `src/gui_2.py` — insert at line 1481 (before `_post_init_callback_result` which is at 1449 — actually place the new helpers AFTER `_post_init_callback_result`)
|
||||
- WHAT: Port tier-2's `src/gui_2.py:1481-1530` verbatim. Adjust imports if needed (`Result`, `ErrorInfo`, `ErrorKind` already imported via `src.result_types`).
|
||||
- HOW: Read tier-2's lines 1481-1530 → copy to master. Strip docstring multi-line commentary to 1-2 lines (NFR3). The function returns `Result[bool]`.
|
||||
- SAFETY: 1-space indent. CRLF. No comments. Match existing `_post_init_callback_result` shape.
|
||||
- RUN: `uv run pytest tests/test_install_default_layout.py -v` — Expected: 5 PASS.
|
||||
- COMMIT: `feat(gui): add _install_default_layout_if_empty + _install_default_layout_if_empty_result helpers`
|
||||
|
||||
- [ ] **Task 2.3: RED test for `_install_default_layout_pre_run_result` (disk-only)**
|
||||
- WHERE: Append to `tests/test_install_default_layout.py`
|
||||
- WHAT: Write 3 tests:
|
||||
1. `test_pre_run_install_empty_dst` — same as 2.1.1 but using `_install_default_layout_pre_run_result` and mocking `_require_warmed("src.layouts")`
|
||||
2. `test_pre_run_install_does_not_call_load_ini_settings_from_memory` — assert `imgui.load_ini_settings_from_memory` was NOT called (imgui not initialized yet)
|
||||
3. `test_pre_run_install_skips_non_empty_dst` — same as 2.1.2
|
||||
- HOW: Same `tmp_path` pattern. Mock `src.layouts.get_layouts_dir` to return `tmp_path / "layouts"`.
|
||||
- SAFETY: 1-space indent. CRLF. Verify `load_ini_settings_from_memory` was NOT called (it's the key behavioral difference vs `_install_default_layout_if_empty`).
|
||||
- RUN: `uv run pytest tests/test_install_default_layout.py -v` — Expected: 3 new FAIL (`ImportError: cannot import name '_install_default_layout_pre_run_result'`).
|
||||
- COMMIT: `test(install): RED phase tests for _install_default_layout_pre_run_result`
|
||||
|
||||
- [ ] **Task 2.4: Implement `_install_default_layout_pre_run_result` in `src/gui_2.py`**
|
||||
- WHERE: `src/gui_2.py` — insert immediately after `_install_default_layout_if_empty_result` (which Task 2.2 placed)
|
||||
- WHAT: Port tier-2's `src/gui_2.py:1543-1590` verbatim. The function reads `get_layouts_dir() / "default.ini"` and writes to `Path.cwd() / "manualslop_layout.ini"`. NO `imgui.load_ini_settings_from_memory` call.
|
||||
- HOW: Read tier-2's lines 1543-1590 → copy to master. Adjust imports.
|
||||
- SAFETY: 1-space indent. CRLF. No comments. The disk-only behavior is the key contract; the function does NOT import or call `imgui`.
|
||||
- RUN: `uv run pytest tests/test_install_default_layout.py -v` — Expected: 8 PASS (5 from 2.1 + 3 new).
|
||||
- COMMIT: `feat(gui): add _install_default_layout_pre_run_result (disk-only, no live-session apply)`
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Wiring (App._post_init + App.run)
|
||||
|
||||
Focus: Wire the install helpers into the app's startup flow.
|
||||
|
||||
- [ ] **Task 3.1: RED test for `App._post_init` calling `_install_default_layout_if_empty_result`**
|
||||
- WHERE: New file `tests/test_app_wiring_install.py`
|
||||
- WHAT: Write 3 tests:
|
||||
1. `test_post_init_calls_install_helper` — instantiate `App`, call `_post_init()`, assert `_install_default_layout_if_empty_result` was called with `src=layouts/default.ini, dst=cwd/manualslop_layout.ini`
|
||||
2. `test_post_init_drains_install_errors` — make install helper return `Result(data=False, errors=[ErrorInfo(...)])`, assert `_startup_timeline_errors` has the entry
|
||||
3. `test_post_init_skips_when_dst_non_empty` — pre-create cwd/manualslop_layout.ini with 5+ `[Window][`, call `_post_init()`, assert install helper was NOT called (or was called but returned `data=False`)
|
||||
- HOW: Use `monkeypatch.setattr(src.gui_2, "_install_default_layout_if_empty_result", lambda app, src, dst: Result(data=True))`. Use `tmp_path` as cwd.
|
||||
- SAFETY: 1-space indent. CRLF. Mock only the boundary helper; verify the call site.
|
||||
- RUN: `uv run pytest tests/test_app_wiring_install.py -v` — Expected: 3 FAIL (call site not yet wired).
|
||||
- COMMIT: `test(gui): RED phase tests for _post_init install wiring`
|
||||
|
||||
- [ ] **Task 3.2: Wire `_install_default_layout_if_empty_result` into `App._post_init`**
|
||||
- WHERE: `src/gui_2.py:566-578` — `_post_init` method. Insert the install call after line 574 (`cb_result = _post_init_callback_result(self)`) and before line 578 (`self._diag_layout_state()`).
|
||||
- WHAT: Add 7 lines:
|
||||
```python
|
||||
from src.layouts import get_layouts_dir
|
||||
src_layout_path: Path = get_layouts_dir() / "default.ini"
|
||||
dst_layout_path: Path = Path.cwd() / "manualslop_layout.ini"
|
||||
install_result: Result[bool] = _install_default_layout_if_empty_result(self, src_layout_path, dst_layout_path)
|
||||
if not install_result.ok:
|
||||
if not hasattr(self, '_startup_timeline_errors'): self._startup_timeline_errors = []
|
||||
self._startup_timeline_errors.append(("_install_default_layout", install_result.errors[0]))
|
||||
```
|
||||
- HOW: Insert after `_post_init_callback_result` block. Match existing 1-space indent in `_post_init`.
|
||||
- SAFETY: 1-space indent. CRLF. The `_startup_timeline_errors` attribute may not exist yet (per existing `_post_init` lines 576, 599 — create it lazily).
|
||||
- RUN: `uv run pytest tests/test_app_wiring_install.py -v` — Expected: 3 PASS.
|
||||
- COMMIT: `feat(gui): wire _install_default_layout_if_empty_result into App._post_init`
|
||||
|
||||
- [ ] **Task 3.3: RED test for `App.run` calling `_install_default_layout_pre_run_result`**
|
||||
- WHERE: Append to `tests/test_app_wiring_install.py`
|
||||
- WHAT: Write 2 tests:
|
||||
1. `test_run_calls_pre_run_install_before_immapp` — mock both `_install_default_layout_pre_run_result` and `_run_immapp_result`, assert order: pre-run install called BEFORE immapp
|
||||
2. `test_run_drains_pre_run_install_errors` — pre-run install returns `Result(data=False, errors=[ErrorInfo])`, assert `_startup_timeline_errors` has the entry
|
||||
- HOW: Use `mock.call_args_list` to verify order. Use `monkeypatch.setattr(src.gui_2, "_install_default_layout_pre_run_result", ...)`.
|
||||
- SAFETY: 1-space indent. CRLF. Mock the pre-run install + immapp helpers; don't actually run immapp.
|
||||
- RUN: `uv run pytest tests/test_app_wiring_install.py -v` — Expected: 2 new FAIL (pre-run call site not wired).
|
||||
- COMMIT: `test(gui): RED phase tests for App.run pre-run install wiring`
|
||||
|
||||
- [ ] **Task 3.4: Wire `_install_default_layout_pre_run_result` into `App.run`**
|
||||
- WHERE: `src/gui_2.py:691` — before `_run_immapp_result(self)` call. Insert 6 lines.
|
||||
- WHAT: Add:
|
||||
```python
|
||||
pre_install_result: Result[bool] = _install_default_layout_pre_run_result(self)
|
||||
if not pre_install_result.ok:
|
||||
err = pre_install_result.errors[0]
|
||||
if hasattr(self, "_startup_timeline_errors"):
|
||||
self._startup_timeline_errors.append(("_install_default_layout_pre_run", err))
|
||||
```
|
||||
- HOW: Insert immediately before `run_result = _run_immapp_result(self)` at line 691. Match existing 1-space indent.
|
||||
- SAFETY: 1-space indent. CRLF. The pre-run install MUST fire before immapp reads the INI from disk.
|
||||
- RUN: `uv run pytest tests/test_app_wiring_install.py -v` — Expected: 5 PASS (3 from 3.1 + 2 from 3.3).
|
||||
- COMMIT: `feat(gui): wire _install_default_layout_pre_run_result into App.run (before immapp)`
|
||||
|
||||
- [ ] **Task 3.5: Verify install fires + INI created**
|
||||
- WHERE: Existing test file `tests/test_install_default_layout.py`
|
||||
- WHAT: Add integration test `test_install_fires_end_to_end` — instantiate `App`, call `_post_init()`, assert cwd/manualslop_layout.ini exists with > 1000 bytes + `[Window][` substring.
|
||||
- HOW: Use `tmp_path` as cwd via `monkeypatch.chdir(tmp_path)`.
|
||||
- SAFETY: 1-space indent. CRLF. Real on-disk assertion (no mocks).
|
||||
- RUN: `uv run pytest tests/test_install_default_layout.py -v` — Expected: 9 PASS.
|
||||
- COMMIT: `test(install): GREEN end-to-end install fires + INI created`
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Surgical Cherry-Picks
|
||||
|
||||
Focus: Apply the 2 surgical fixes that don't require new infrastructure.
|
||||
|
||||
- [ ] **Task 4.1: Cherry-pick orphan-end-child fix**
|
||||
- WHERE: `src/gui_2.py:6990` — delete the line `imgui.end_child()` inside the `except (TypeError, AttributeError):` block in `render_tier_stream_panel`.
|
||||
- WHAT: Apply tier-2's `c2155593` 1-line deletion. The orphan `end_child()` at line 6990 fires with no matching `begin_child()` when the try block raises (e.g. `len(None)`).
|
||||
- HOW: Read `src/gui_2.py:6984-6991` → delete line 6990 (the `imgui.end_child()` inside except). Keep line 6988 (the correct one inside try). Keep `pass` on line 6991.
|
||||
- SAFETY: 1-space indent. CRLF. Preserve the `try/except` structure. The deleted line is the only change.
|
||||
- RUN: `uv run python scripts/check_imgui_scopes.py src/gui_2.py` — Expected: 3 "extra end" warnings (down from 4). The 4925 + 7094 + 8810 warnings remain (other code); the 6990 one should be gone.
|
||||
- COMMIT: `fix(gui): remove orphan imgui.end_child() in render_tier_stream_panel except handler`
|
||||
|
||||
- [ ] **Task 4.2: Cherry-pick reset_layout dead-path cleanup**
|
||||
- WHERE: `src/commands.py:268` — delete the line `os.path.join("tests", "artifacts", "live_gui_workspace", "manualslop_layout.ini"),` from the `layout_paths` list inside `reset_layout`.
|
||||
- WHAT: Apply tier-2's `3b966288`. The `reset_layout` command should not reference test fixtures in production code.
|
||||
- HOW: Read `src/commands.py:365-380` → identify the line that hardcodes `tests/artifacts/manualslop_layout_default.ini` → delete it. If the surrounding logic needs adjustment (e.g. fallback to a different path), update the fallback.
|
||||
- SAFETY: 1-space indent. CRLF. The behavior of `reset_layout` should be preserved — it still resets the layout, just from a different source path.
|
||||
- RUN: `uv run pytest tests/test_commands.py -v` — Expected: PASS (the existing tests cover the reset_layout behavior).
|
||||
- COMMIT: `chore(commands): remove dead test-fixture path from reset_layout`
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Layer 1 Verification — Per-Panel Render Sentinel
|
||||
|
||||
Focus: The "panels actually render" test that catches the original bug.
|
||||
|
||||
- [ ] **Task 5.1: RED test for per-panel render size check**
|
||||
- WHERE: New file `tests/test_panels_visible_after_install.py`
|
||||
- WHAT: Write 3 tests:
|
||||
1. `test_panels_visible_after_install` — use `live_gui` fixture, wait for first frame, iterate `app.show_windows` for entries where `value == True`, assert each has nonzero render size via `imgui.find_window_viewport(name).size.x > 0`
|
||||
2. `test_panel_invisible_when_show_windows_false` — same loop, but verify panels with `value == False` are NOT in `find_window_viewport` results
|
||||
3. `test_panel_render_size_is_correct_window` — assert `find_window_viewport("AI Settings").size.x > 100 AND .size.y > 50` (sanity: visible panels have meaningful size, not 0)
|
||||
- HOW: Use `live_gui` fixture. Poll for first frame via `client.wait_for_event` (not `time.sleep`). Use `imgui.find_window_viewport(name)` API.
|
||||
- SAFETY: Poll-loop, not `time.sleep`. 1-space indent. CRLF. Skip test on non-Windows (`@pytest.mark.skipif(sys.platform != "win32")`).
|
||||
- RUN: `uv run pytest tests/test_panels_visible_after_install.py -v` — Expected: PASS on first try IF install infrastructure works (since Phase 1-3 is done by now). The value of this test is regression detection, not initial GREEN.
|
||||
- COMMIT: `test(visual): Layer 1 per-panel render sentinel (catches empty-panels regression)`
|
||||
|
||||
- [ ] **Task 5.2: Verify sentinel catches the regression (negative test mode)**
|
||||
- WHERE: Append to `tests/test_panels_visible_after_install.py`
|
||||
- WHAT: Write `test_sentinel_catches_empty_panels` — use `live_gui` fixture, BUT monkey-patch `_install_default_layout_pre_run_result` to return `Result(data=False)` (skip install). Also, pre-create cwd/manualslop_layout.ini with content that omits all `[Window][X]` entries (just an empty INI). Assert the test FAILS.
|
||||
- HOW: Use `monkeypatch.setattr`. The sentinel should detect that 8 default-visible panels all have zero render size.
|
||||
- SAFETY: This test verifies the sentinel's REGRESSION CATCH ability. It should NOT pass — its job is to confirm the sentinel works.
|
||||
- RUN: `uv run pytest tests/test_panels_visible_after_install.py::test_sentinel_catches_empty_panels -v` — Expected: FAIL with assertion error listing 8 panels with zero render size.
|
||||
- COMMIT: `test(visual): RED negative test — sentinel catches empty-panels regression`
|
||||
|
||||
- [ ] **Task 5.3: Verify sentinel catches the original bug (mock the import failure)**
|
||||
- WHERE: Append to `tests/test_panels_visible_after_install.py`
|
||||
- WHAT: Write `test_sentinel_catches_render_main_interface_no_op` — use `live_gui` fixture, monkey-patch `src.gui_2.render_main_interface` to be a no-op (`lambda app: None`). Assert the sentinel FAILS (panels don't render).
|
||||
- HOW: This simulates the original tier-2 bug: `render_main_interface` is a no-op due to ModuleNotFoundError.
|
||||
- SAFETY: Use `monkeypatch.setattr` to swap the function reference at module level.
|
||||
- RUN: `uv run pytest tests/test_panels_visible_after_install.py::test_sentinel_catches_render_main_interface_no_op -v` — Expected: FAIL with assertion error listing 8 panels with zero render size.
|
||||
- COMMIT: `test(visual): RED negative test — sentinel catches render_main_interface no-op`
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Layer 2 Verification — Win32 PrintWindow Pixel Baseline
|
||||
|
||||
Focus: The HARD pixel-diff test that catches ALL visual regressions.
|
||||
|
||||
- [ ] **Task 6.1: RED test for Win32 PrintWindow capture**
|
||||
- WHERE: New file `tests/test_visual_baseline_default.py`
|
||||
- WHAT: Write 4 tests:
|
||||
1. `test_capture_gui_window_pixels` — use `live_gui` fixture, wait for first frame, call `_capture_gui_window_png()`, assert the returned PNG file exists with size > 0
|
||||
2. `test_capture_returns_png_with_correct_dimensions` — assert PNG dimensions match the forced viewport (1680x1050 from F6.1 env var)
|
||||
3. `test_capture_handles_missing_hwnd` — simulate window-not-found → return `Result(data=None, errors=[ErrorInfo])`
|
||||
4. `test_capture_does_not_crash_on_zero_size` — simulate hwnd with zero-size window → return `Result(data=None, errors=[ErrorInfo])` (no crash)
|
||||
- HOW: Import `_capture_gui_window_png` from `src.gui_2`. Use `live_gui` fixture with `MANUAL_SLOP_TEST_VIEWPORT=1680x1050` + `MANUAL_SLOP_TEST_THEME=dark` env vars.
|
||||
- SAFETY: 1-space indent. CRLF. Skip on non-Windows. Use `tmp_path` for PNG output.
|
||||
- RUN: `uv run pytest tests/test_visual_baseline_default.py -v` — Expected: 4 FAIL (`ImportError: cannot import name '_capture_gui_window_png'`).
|
||||
- COMMIT: `test(visual): RED phase tests for Win32 PrintWindow capture`
|
||||
|
||||
- [ ] **Task 6.2: Implement `_capture_gui_window_png` in `src/gui_2.py`**
|
||||
- WHERE: `src/gui_2.py` — insert after `_install_default_layout_pre_run_result`
|
||||
- WHAT: Port the Win32 PrintWindow capture logic. Find imgui window via `win32gui.FindWindow(None, "manual slop")`; allocate DC + bitmap; call `win32gui.PrintWindow(hwnd, hdc, win32con.PW_RENDERFULLCONTENT)`; convert to PNG via `Pillow.Image.frombuffer(...)`; save to given `Path`. Returns `Result[Path]`.
|
||||
- HOW: Import `win32gui`, `win32con`, `win32ui` from `pywin32`. Import `PIL.Image`. The function signature: `_capture_gui_window_png(out_path: Path) -> Result[Path]`.
|
||||
- SAFETY: 1-space indent. CRLF. No comments. Wrap each Win32 call in try/except returning `ErrorInfo`. Use `win32gui.DestroyWindow(hwnd)` after capture (cleanup).
|
||||
- RUN: `uv run pytest tests/test_visual_baseline_default.py -v` — Expected: 4 PASS.
|
||||
- COMMIT: `feat(gui): add _capture_gui_window_png via Win32 PrintWindow + Pillow`
|
||||
|
||||
- [ ] **Task 6.3: Generate baseline PNG**
|
||||
- WHERE: New file `tests/artifacts/visual_baseline_default.png`
|
||||
- WHAT: Capture the running GUI's pixels after install fires + panels render. This is the "known good" reference.
|
||||
- HOW: Run `uv run python -m pytest tests/test_visual_baseline_default.py::test_capture_gui_window_pixels --capture=tee-sys -s` and manually save the output PNG. OR: write a one-shot helper script `scripts/capture_visual_baseline.py` that spawns the app, waits for first frame, calls `_capture_gui_window_png(artifacts/visual_baseline_default.png)`, exits.
|
||||
- SAFETY: 1-space indent. CRLF. The baseline PNG must be captured AFTER all install infrastructure is in place. Verify the PNG visually (user's eyes) before committing.
|
||||
- RUN: `uv run python scripts/capture_visual_baseline.py` — Expected: writes `tests/artifacts/visual_baseline_default.png` (~50-200 KB depending on viewport size).
|
||||
- COMMIT: `feat(visual): commit visual_baseline_default.png (the known-good pixel reference)`
|
||||
|
||||
- [ ] **Task 6.4: RED test for pixel diff comparison**
|
||||
- WHERE: Append to `tests/test_visual_baseline_default.py`
|
||||
- WHAT: Write 3 tests:
|
||||
1. `test_pixel_diff_below_threshold` — capture current + load baseline → assert diff < 1%
|
||||
2. `test_pixel_diff_above_threshold_on_corrupt_ini` — corrupt the INI (delete `[Docking][Data]` line) + capture → assert diff > 5% (catches regression)
|
||||
3. `test_pixel_diff_threshold_configurable` — pass `--threshold 0.05` → assert behavior matches
|
||||
- HOW: Use `_compute_pixel_diff(baseline_path, current_path) -> float`. The function: load both via `Pillow.Image.open()`, convert to RGB, compute `numpy.abs(np.array(a) - np.array(b)).mean() / 255.0`.
|
||||
- SAFETY: 1-space indent. CRLF. Skip on non-Windows. Threshold default = 0.01 (1%).
|
||||
- RUN: `uv run pytest tests/test_visual_baseline_default.py -v` — Expected: 3 new FAIL (`ImportError: cannot import name '_compute_pixel_diff'`).
|
||||
- COMMIT: `test(visual): RED phase tests for pixel diff comparison`
|
||||
|
||||
- [ ] **Task 6.5: Implement `_compute_pixel_diff` in `src/gui_2.py`**
|
||||
- WHERE: `src/gui_2.py` — insert after `_capture_gui_window_png`
|
||||
- WHAT: Compare two PNGs and return pixel diff as float (0.0-1.0).
|
||||
- HOW: Load both via `Pillow.Image.open(path).convert("RGB")`. Convert to numpy arrays. Compute `numpy.abs(a - b).mean() / 255.0`. Return the float.
|
||||
- SAFETY: 1-space indent. CRLF. Handle size mismatch (resize to larger dim). Handle missing files → return 1.0 (100% diff = max divergence).
|
||||
- RUN: `uv run pytest tests/test_visual_baseline_default.py -v` — Expected: 7 PASS (4 from 6.1 + 3 new).
|
||||
- COMMIT: `feat(gui): add _compute_pixel_diff (numpy-based pixel comparison)`
|
||||
|
||||
---
|
||||
|
||||
## Phase 7: Layer 3 Verification — Forced Test Viewport + Theme
|
||||
|
||||
Focus: Make the baseline deterministic so pixel diff is meaningful.
|
||||
|
||||
- [ ] **Task 7.1: RED test for `MANUAL_SLOP_TEST_VIEWPORT` env var**
|
||||
- WHERE: New file `tests/test_test_mode_env_vars.py`
|
||||
- WHAT: Write 2 tests:
|
||||
1. `test_viewport_env_var_overrides_default` — spawn subprocess with `MANUAL_SLOP_TEST_VIEWPORT=1920x1080` env var → assert `App.run()` set `runner_params.app_window_params.window_geometry.size = (1920, 1080)`
|
||||
2. `test_viewport_env_var_unset_uses_default` — spawn without env var → assert size = (1680, 1200) (current default at line 651)
|
||||
- HOW: Use `subprocess` to spawn `sloppy.py` with env vars. Inspect via the `/api/gui` Hook API endpoint after launch.
|
||||
- SAFETY: 1-space indent. CRLF. Use `subprocess.run` with timeout. Clean up subprocess on test teardown via `kill_process_tree` fixture.
|
||||
- RUN: `uv run pytest tests/test_test_mode_env_vars.py -v` — Expected: 2 FAIL (env var not honored).
|
||||
- COMMIT: `test(env): RED phase tests for MANUAL_SLOP_TEST_VIEWPORT env var`
|
||||
|
||||
- [ ] **Task 7.2: Implement `MANUAL_SLOP_TEST_VIEWPORT` parsing in `App.run`**
|
||||
- WHERE: `src/gui_2.py:651` — before `self.runner_params.app_window_params.window_geometry.size = (1680, 1200)`, add the env var parsing.
|
||||
- WHAT: Read env var. If set and matches `WxH` pattern, override the size.
|
||||
- HOW: Add 5 lines before line 651:
|
||||
```python
|
||||
_test_viewport = os.environ.get("MANUAL_SLOP_TEST_VIEWPORT")
|
||||
if _test_viewport and "x" in _test_viewport:
|
||||
_w, _h = _test_viewport.split("x", 1)
|
||||
_w, _h = int(_w), int(_h)
|
||||
else:
|
||||
_w, _h = 1680, 1200
|
||||
self.runner_params.app_window_params.window_geometry.size = (_w, _h)
|
||||
```
|
||||
- SAFETY: 1-space indent. CRLF. Wrap the parsing in try/except (return default on ValueError).
|
||||
- RUN: `uv run pytest tests/test_test_mode_env_vars.py -v` — Expected: 2 PASS.
|
||||
- COMMIT: `feat(gui): honor MANUAL_SLOP_TEST_VIEWPORT env var (Layer 3 forced viewport)`
|
||||
|
||||
- [ ] **Task 7.3: RED test for `MANUAL_SLOP_TEST_THEME` env var**
|
||||
- WHERE: Append to `tests/test_test_mode_env_vars.py`
|
||||
- WHAT: Write 2 tests:
|
||||
1. `test_theme_env_var_overrides_default` — spawn with `MANUAL_SLOP_TEST_THEME=dark` → assert `runner_params.imgui_window_params.tweaked_theme` is `ImGuiTheme_.ImGuiColorsDark`
|
||||
2. `test_theme_env_var_unset_uses_default` — spawn without env var → assert theme is NOT forced
|
||||
- HOW: Same `subprocess` + Hook API pattern.
|
||||
- SAFETY: 1-space indent. CRLF.
|
||||
- RUN: `uv run pytest tests/test_test_mode_env_vars.py -v` — Expected: 2 new FAIL (env var not honored).
|
||||
- COMMIT: `test(env): RED phase tests for MANUAL_SLOP_TEST_THEME env var`
|
||||
|
||||
- [ ] **Task 7.4: Implement `MANUAL_SLOP_TEST_THEME` parsing in `App.run`**
|
||||
- WHERE: `src/gui_2.py:654` — before `self.runner_params.imgui_window_params.tweaked_theme = theme.get_tweaked_theme()`, add the env var parsing.
|
||||
- WHAT: Read env var. If set to `dark`, force theme to `hello_imgui.ImGuiTheme_.ImGuiColorsDark`.
|
||||
- HOW: Add 5 lines before line 654:
|
||||
```python
|
||||
_test_theme = os.environ.get("MANUAL_SLOP_TEST_THEME")
|
||||
if _test_theme == "dark":
|
||||
self.runner_params.imgui_window_params.tweaked_theme = hello_imgui.ImGuiTheme_.ImGuiColorsDark
|
||||
else:
|
||||
self.runner_params.imgui_window_params.tweaked_theme = theme.get_tweaked_theme()
|
||||
```
|
||||
- SAFETY: 1-space indent. CRLF. The original `theme.get_tweaked_theme()` call becomes the `else` branch.
|
||||
- RUN: `uv run pytest tests/test_test_mode_env_vars.py -v` — Expected: 4 PASS.
|
||||
- COMMIT: `feat(gui): honor MANUAL_SLOP_TEST_THEME env var (Layer 3 forced theme)`
|
||||
|
||||
---
|
||||
|
||||
## Phase 8: Layer 4 Verification — Cannot-Skip Gates
|
||||
|
||||
Focus: Make the verification infrastructure impossible to ignore.
|
||||
|
||||
- [ ] **Task 8.1: Create `scripts/check_visual_baseline.py`**
|
||||
- WHERE: New file `scripts/check_visual_baseline.py`
|
||||
- WHAT: Standalone CLI script that compares two PNGs and exits 1 on diff > threshold.
|
||||
- HOW: Args: `--baseline <path>` (default: `tests/artifacts/visual_baseline_default.png`), `--current <path>` (required), `--threshold <float>` (default: 0.01). Uses `Pillow` + `numpy` for diff. Returns exit code 0 if diff ≤ threshold, exit code 1 otherwise. Print diff percentage to stdout. Use the same `_compute_pixel_diff` logic from Task 6.5.
|
||||
- SAFETY: 1-space indent. CRLF. Use `argparse`. Handle missing files gracefully (exit 1 + error message).
|
||||
- RUN: `uv run python scripts/check_visual_baseline.py --help` — Expected: usage message. `uv run python scripts/check_visual_baseline.py --current tests/artifacts/visual_baseline_default.png --baseline tests/artifacts/visual_baseline_default.png` — Expected: `diff: 0.0000 PASS`.
|
||||
- COMMIT: `feat(visual): add scripts/check_visual_baseline.py (Layer 4 standalone CI gate)`
|
||||
|
||||
- [ ] **Task 8.2: Wire `check_visual_baseline.py` into `scripts/run_tests_batched.py`**
|
||||
- WHERE: `scripts/run_tests_batched.py` — add a new tier (or extend an existing one) that runs `tests/test_visual_baseline_default.py` + `tests/test_panels_visible_after_install.py` + `scripts/check_visual_baseline.py`.
|
||||
- WHAT: Add a tier (e.g. `tier_visual`) to the batched runner config. The tier runs after `tier3` and before the smoke tier.
|
||||
- HOW: Read `scripts/run_tests_batched.py` config → add `tier_visual` → list the 3 commands.
|
||||
- SAFETY: 1-space indent. CRLF. Don't break existing tiers.
|
||||
- RUN: `uv run python scripts/run_tests_batched.py --tier visual` — Expected: 7 tests pass (4 + 3 from Phase 5-6).
|
||||
- COMMIT: `chore(tests): wire Layer 1+2 visual tests into scripts/run_tests_batched.py`
|
||||
|
||||
- [ ] **Task 8.3: Write `docs/guide_visual_verification.md`**
|
||||
- WHERE: New file `docs/guide_visual_verification.md`
|
||||
- WHAT: 200-300 line guide documenting:
|
||||
- The 4 layers (per-panel sentinel, pixel baseline, forced viewport/theme, cannot-skip gates)
|
||||
- How to add a new visual baseline
|
||||
- How to update an existing baseline (after a deliberate UI change)
|
||||
- The env-var protocol (`MANUAL_SLOP_TEST_VIEWPORT`, `MANUAL_SLOP_TEST_THEME`)
|
||||
- The `VERIFIED-<YYYYMMDD>` tag protocol
|
||||
- When to use imgui_test_engine vs PrintWindow (the trade-offs)
|
||||
- HOW: Write as a markdown guide with code blocks + cross-references to `docs/guide_testing.md` + `docs/guide_gui_2.md`.
|
||||
- SAFETY: Markdown formatting consistent with other `docs/guide_*.md` files.
|
||||
- RUN: N/A (docs file).
|
||||
- COMMIT: `docs(visual-verification): add guide for the 4-layer visual verification protocol`
|
||||
|
||||
- [ ] **Task 8.4: Update `conductor/tracks.md` schema**
|
||||
- WHERE: `conductor/tracks.md` — find the schema section (or add a new "Track Completion Gates" section).
|
||||
- WHAT: Add a new section documenting the `VERIFIED-<YYYYMMDD>` tag requirement for tracks that touch `src/gui_2.py`. Tracks that ship without the tag are NOT marked `[x]`.
|
||||
- HOW: Read `conductor/tracks.md` → find the schema → add the new gate.
|
||||
- SAFETY: Markdown formatting consistent. Cross-reference `docs/guide_visual_verification.md`.
|
||||
- RUN: N/A (docs file).
|
||||
- COMMIT: `docs(tracks): add VERIFIED-<date> tag requirement for tracks touching src/gui_2.py`
|
||||
|
||||
- [ ] **Task 8.5: Update `docs/Readme.md` to reference the new guide**
|
||||
- WHERE: `docs/Readme.md` — find the "Per-Source-File Deep Dives" section (or equivalent) → add `docs/guide_visual_verification.md` entry.
|
||||
- WHAT: Add a new bullet + 1-line description.
|
||||
- HOW: Read `docs/Readme.md` → add the entry.
|
||||
- SAFETY: Match existing entry format.
|
||||
- RUN: N/A (docs file).
|
||||
- COMMIT: `docs(readme): cross-reference guide_visual_verification.md`
|
||||
|
||||
---
|
||||
|
||||
## Phase 9: End-to-End Verification + Negative Test + Track Completion
|
||||
|
||||
Focus: Prove the verification infrastructure actually catches regressions, then close out the track.
|
||||
|
||||
- [ ] **Task 9.1: Write `tests/test_visual_baseline_catches_corrupt_ini.py`**
|
||||
- WHERE: New file `tests/test_visual_baseline_catches_corrupt_ini.py`
|
||||
- WHAT: Write 1 test that uses `live_gui` fixture; AFTER install fires, manually delete the `[Docking][Data]` line from cwd/manualslop_layout.ini; re-launch + capture; assert pixel diff > 5%.
|
||||
- HOW: Spawn app → wait for first frame → corrupt INI → quit → re-launch → wait for first frame → capture screenshot → compare to baseline.
|
||||
- SAFETY: 1-space indent. CRLF. Use `kill_process_tree` fixture for cleanup. Skip on non-Windows.
|
||||
- RUN: `uv run pytest tests/test_visual_baseline_catches_corrupt_ini.py -v` — Expected: PASS (the diff should be > 5% because panels don't render visibly).
|
||||
- COMMIT: `test(visual): negative test — corrupted INI catches the regression (FR8)`
|
||||
|
||||
- [ ] **Task 9.2: Run full test batch**
|
||||
- WHERE: All test files added in Phase 1-9
|
||||
- WHAT: Run `scripts/run_tests_batched.py` end-to-end. Verify all tiers PASS.
|
||||
- HOW: `uv run python scripts/run_tests_batched.py` — runs the full batch (not just `tier_visual`).
|
||||
- SAFETY: If any tier fails, STOP. Report to user. Do NOT mark track complete.
|
||||
- RUN: Expected: all 11 tiers PASS. If a tier fails, debug per `conductor/workflow.md` "Deduction Loop" rule (max 2 runs).
|
||||
- COMMIT: N/A (verification only).
|
||||
|
||||
- [ ] **Task 9.3: Manual visual verification gate**
|
||||
- WHERE: User's machine
|
||||
- WHAT: User runs `uv run sloppy.py` from master. User confirms panels render visibly (Project Settings, Files & Media, AI Settings, Operations Hub, Theme on left; Discussion Hub, Log Management, Diagnostics on right).
|
||||
- HOW: User reports back. If panels DO render visibly → proceed. If panels DON'T render → STOP, debug, report.
|
||||
- SAFETY: N/A (manual gate).
|
||||
- COMMIT: N/A (manual verification only).
|
||||
|
||||
- [ ] **Task 9.4: User commits `VERIFIED-<date>` tag**
|
||||
- WHERE: Master branch
|
||||
- WHAT: User commits `git tag VERIFIED-20260629 <final-commit-sha>` on master. Documents the visual verification.
|
||||
- HOW: `git tag VERIFIED-20260629 <sha>`. Add to track completion checklist.
|
||||
- SAFETY: HARD GATE. Without this tag, the track is NOT marked complete in `conductor/tracks.md`.
|
||||
- COMMIT: N/A (tag, not commit). But attach a git note to the final commit: `git notes add -m "VISUALLY VERIFIED: panels render correctly via uv run sloppy.py from master"`.
|
||||
|
||||
- [ ] **Task 9.5: Write `docs/reports/TRACK_COMPLETION_default_layout_extract_20260629.md`**
|
||||
- WHERE: New file `docs/reports/TRACK_COMPLETION_default_layout_extract_20260629.md`
|
||||
- WHAT: 100-200 line report documenting:
|
||||
- What was extracted (per FR1-FR3)
|
||||
- What was built (per FR4-FR7)
|
||||
- Test results (per FR8)
|
||||
- User verification (per 9.3)
|
||||
- Follow-up tracks (Fleury migration, imgui_test_engine integration)
|
||||
- Tier-2 archival status (user's responsibility)
|
||||
- HOW: Markdown report. Cross-reference `docs/reports/PANEL_VISIBILITY_DEBUG_REPORT_20260629.md` + `conductor/tracks/default_layout_extract_20260629/spec.md`.
|
||||
- SAFETY: 100-200 lines max. Concise.
|
||||
- COMMIT: `docs(reports): TRACK_COMPLETION_default_layout_extract_20260629`
|
||||
|
||||
- [ ] **Task 9.6: Update `conductor/tracks.md` to mark this track complete**
|
||||
- WHERE: `conductor/tracks.md` — find the row for `default_layout_extract_20260629` → mark `[x]` (with `VERIFIED-20260629` tag referenced).
|
||||
- WHAT: Update the row.
|
||||
- HOW: Read `conductor/tracks.md` → find the row → update.
|
||||
- SAFETY: HARD GATE. The `[x]` requires the `VERIFIED-<date>` tag to exist. If absent, leave the row as `[ ]`.
|
||||
- COMMIT: `conductor(tracks): mark default_layout_extract_20260629 complete (with VERIFIED-20260629 tag)`
|
||||
|
||||
- [ ] **Task 9.7: Conductor - User Manual Verification (Protocol in workflow.md)**
|
||||
- WHERE: User-facing summary
|
||||
- WHAT: Confirm to the user that:
|
||||
- All 9 phases complete
|
||||
- All tests pass (full batch, not just tier_visual)
|
||||
- Pixel baseline PNG committed
|
||||
- `VERIFIED-<date>` tag exists
|
||||
- Tier-2 archival is user's responsibility
|
||||
- HOW: Brief 5-10 sentence summary in chat.
|
||||
- SAFETY: HARD GATE. Do NOT claim "track complete" without the tag + the user's confirmation.
|
||||
|
||||
---
|
||||
|
||||
## Self-Review (per writing-plans skill)
|
||||
|
||||
**1. Spec coverage:**
|
||||
- G1 (FR1.1-FR1.4) → Phase 1 tasks ✓
|
||||
- G2 (FR2.1-FR2.5) → Phase 2-3 tasks ✓
|
||||
- G3 (FR3.2) → Phase 4 task 4.2 ✓
|
||||
- G4 (FR3.1) → Phase 4 task 4.1 ✓
|
||||
- G5 Layer 1 (FR4.1-FR4.4) → Phase 5 tasks ✓
|
||||
- G5 Layer 2 (FR5.1-FR5.6) → Phase 6 tasks ✓
|
||||
- G5 Layer 3 (FR6.1-FR6.4) → Phase 7 tasks ✓
|
||||
- G5 Layer 4 (FR7.1-F7.4) → Phase 8 tasks ✓
|
||||
- G6 (FR8.1-FR8.2) → Phase 9 task 9.1 ✓
|
||||
|
||||
**2. Placeholder scan:**
|
||||
- No "TBD", "TODO", "implement later", "fill in details"
|
||||
- No "add appropriate error handling" — each error case is specified
|
||||
- No "similar to Task N" — each task is self-contained
|
||||
- No steps without code blocks where code is required
|
||||
|
||||
**3. Type consistency:**
|
||||
- `_install_default_layout_if_empty` → `Result[bool]` (Task 2.2, 3.1, 3.2) ✓
|
||||
- `_install_default_layout_if_empty_result` → `Result[bool]` (Task 2.2, 3.1, 3.2) ✓
|
||||
- `_install_default_layout_pre_run_result` → `Result[bool]` (Task 2.4, 3.3, 3.4) ✓
|
||||
- `_capture_gui_window_png` → `Result[Path]` (Task 6.1, 6.2) ✓
|
||||
- `_compute_pixel_diff(baseline, current)` → `float` (Task 6.4, 6.5) ✓
|
||||
- `LayoutFile` → `@dataclass(frozen=True, slots=True)` (Task 1.2) ✓
|
||||
- `Result`, `ErrorInfo`, `ErrorKind` from `src.result_types` (consistent throughout) ✓
|
||||
|
||||
**4. Spec coverage check:**
|
||||
- Spec §FR1.1 → Task 1.6 ✓
|
||||
- Spec §FR1.2 → Task 1.2 ✓
|
||||
- Spec §FR1.3 → Tasks 1.3, 1.4 ✓
|
||||
- Spec §FR1.4 → covered by Task 1.6 (test for INI existence) ✓
|
||||
- Spec §FR2.1 → Task 2.2 ✓
|
||||
- Spec §FR2.2 → Task 2.2 ✓
|
||||
- Spec §FR2.3 → Task 2.4 ✓
|
||||
- Spec §FR2.4 → Task 3.2 ✓
|
||||
- Spec §FR2.5 → Task 3.4 ✓
|
||||
- Spec §FR3.1 → Task 4.1 ✓
|
||||
- Spec §FR3.2 → Task 4.2 ✓
|
||||
- Spec §FR4.1-FR4.4 → Phase 5 tasks ✓
|
||||
- Spec §FR5.1-FR5.6 → Phase 6 tasks ✓
|
||||
- Spec §FR6.1-FR6.4 → Phase 7 tasks ✓
|
||||
- Spec §FR7.1-FR7.4 → Phase 8 tasks ✓
|
||||
- Spec §FR8.1-FR8.2 → Task 9.1 ✓
|
||||
|
||||
No gaps found.
|
||||
|
||||
## Summary
|
||||
|
||||
- **9 phases**, **36 tasks** (each surgical with WHERE/WHAT/HOW/SAFETY/COMMIT)
|
||||
- **3 new files**: `src/layouts.py`, `layouts/default.ini`, `tests/artifacts/visual_baseline_default.png`, `scripts/check_visual_baseline.py`, `docs/guide_visual_verification.md`
|
||||
- **6 modified files**: `src/gui_2.py`, `src/paths.py`, `src/commands.py`, `scripts/run_tests_batched.py`, `conductor/tracks.md`, `docs/Readme.md`
|
||||
- **5 new test files**: `tests/test_layouts.py`, `tests/test_paths_layouts.py`, `tests/test_layouts_bundled.py`, `tests/test_install_default_layout.py`, `tests/test_app_wiring_install.py`, `tests/test_panels_visible_after_install.py`, `tests/test_visual_baseline_default.py`, `tests/test_test_mode_env_vars.py`, `tests/test_visual_baseline_catches_corrupt_ini.py`
|
||||
- **~36 atomic commits** (1 per task)
|
||||
- **HARD verification gates**: Layer 1 sentinel + Layer 2 pixel baseline + Layer 3 forced viewport/theme + Layer 4 cannot-skip tags
|
||||
|
||||
This is the "no slippage" plan. Each task is a 2-5 minute action. Each has a commit. The verification infrastructure makes the regression impossible to reintroduce without CI catching it.
|
||||
@@ -0,0 +1,226 @@
|
||||
# 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:1776` — `from 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-703` — `App.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-578` — `App._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-6991` — `render_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-275` — `reset_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-706` — `App.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-582` — `App._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.md`** — `Result[T]` + `ErrorInfo` + `ErrorKind` usage. The install helpers return `Result[bool]` per this styleguide.
|
||||
- **`conductor/code_styleguides/type_aliases.md`** — `Metadata = 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.
|
||||
@@ -0,0 +1,95 @@
|
||||
# Track state for default_layout_extract_20260629
|
||||
# Updated by Tier 2 Tech Lead as tasks complete
|
||||
|
||||
[meta]
|
||||
track_id = "default_layout_extract_20260629"
|
||||
name = "Default Layout Extract + Hard Visual Verification"
|
||||
status = "active"
|
||||
current_phase = 0
|
||||
last_updated = "2026-06-29"
|
||||
|
||||
[blocked_by]
|
||||
# None — this track is independent (replaces default_layout_install_20260629 which is superseded)
|
||||
|
||||
[blocks]
|
||||
# Tracks that depend on this one
|
||||
panel_defs_fleury_migration = "deferred (consumes LayoutFile + get_layouts_dir)"
|
||||
render_persona_editor_window_fix = "deferred (Layer 1 sentinel catches the empty-content bug)"
|
||||
test_engine_integration_20260627 = "in_progress (separate track)"
|
||||
|
||||
[phases]
|
||||
phase_1 = { status = "pending", checkpointsha = "", name = "Asset Foundation (layouts/ + src/layouts.py + get_layouts_dir)" }
|
||||
phase_2 = { status = "pending", checkpointsha = "", name = "Install Helpers (_install_default_layout_if_empty + pre_run)" }
|
||||
phase_3 = { status = "pending", checkpointsha = "", name = "Wiring (App._post_init + App.run)" }
|
||||
phase_4 = { status = "pending", checkpointsha = "", name = "Surgical Cherry-Picks (orphan end_child + reset_layout)" }
|
||||
phase_5 = { status = "pending", checkpointsha = "", name = "Layer 1 Sentinel (per-panel render size check)" }
|
||||
phase_6 = { status = "pending", checkpointsha = "", name = "Layer 2 Pixel Baseline (Win32 PrintWindow)" }
|
||||
phase_7 = { status = "pending", checkpointsha = "", name = "Layer 3 Forced Viewport/Theme (env vars)" }
|
||||
phase_8 = { status = "pending", checkpointsha = "", name = "Layer 4 Cannot-Skip Gates (CI + tag)" }
|
||||
phase_9 = { status = "pending", checkpointsha = "", name = "Negative Test + End-to-End + Track Completion" }
|
||||
|
||||
[tasks]
|
||||
# Phase 1
|
||||
t1_1 = { status = "pending", commit_sha = "", description = "RED test for src/layouts.py:load_layouts_from_dir" }
|
||||
t1_2 = { status = "pending", commit_sha = "", description = "Create src/layouts.py (port fresh from tier-2)" }
|
||||
t1_3 = { status = "pending", commit_sha = "", description = "RED test for src/paths.py:get_global_layouts_path" }
|
||||
t1_4 = { status = "pending", commit_sha = "", description = "Add get_global_layouts_path() + SLOP_GLOBAL_LAYOUTS env override" }
|
||||
t1_5 = { status = "pending", commit_sha = "", description = "RED test for bundled layouts/default.ini structure" }
|
||||
t1_6 = { status = "pending", commit_sha = "", description = "Port layouts/default.ini to master (8 [Window] + [Docking])" }
|
||||
# Phase 2
|
||||
t2_1 = { status = "pending", commit_sha = "", description = "RED test for _install_default_layout_if_empty (5 cases)" }
|
||||
t2_2 = { status = "pending", commit_sha = "", description = "Implement _install_default_layout_if_empty + _result wrapper" }
|
||||
t2_3 = { status = "pending", commit_sha = "", description = "RED test for _install_default_layout_pre_run_result (disk-only)" }
|
||||
t2_4 = { status = "pending", commit_sha = "", description = "Implement _install_default_layout_pre_run_result" }
|
||||
# Phase 3
|
||||
t3_1 = { status = "pending", commit_sha = "", description = "RED test for App._post_init calling install helper" }
|
||||
t3_2 = { status = "pending", commit_sha = "", description = "Wire _install_default_layout_if_empty_result into App._post_init" }
|
||||
t3_3 = { status = "pending", commit_sha = "", description = "RED test for App.run calling pre-run install before immapp" }
|
||||
t3_4 = { status = "pending", commit_sha = "", description = "Wire _install_default_layout_pre_run_result into App.run" }
|
||||
t3_5 = { status = "pending", commit_sha = "", description = "GREEN end-to-end install fires + INI created" }
|
||||
# Phase 4
|
||||
t4_1 = { status = "pending", commit_sha = "", description = "Cherry-pick c2155593 (remove orphan imgui.end_child at line 6990)" }
|
||||
t4_2 = { status = "pending", commit_sha = "", description = "Cherry-pick 3b966288 (remove dead test-fixture path from reset_layout)" }
|
||||
# Phase 5
|
||||
t5_1 = { status = "pending", commit_sha = "", description = "RED test for per-panel render size check (Layer 1)" }
|
||||
t5_2 = { status = "pending", commit_sha = "", description = "Verify sentinel catches empty-panels regression (negative test)" }
|
||||
t5_3 = { status = "pending", commit_sha = "", description = "Verify sentinel catches render_main_interface no-op (negative test)" }
|
||||
# Phase 6
|
||||
t6_1 = { status = "pending", commit_sha = "", description = "RED test for Win32 PrintWindow capture (Layer 2)" }
|
||||
t6_2 = { status = "pending", commit_sha = "", description = "Implement _capture_gui_window_png (PrintWindow + Pillow)" }
|
||||
t6_3 = { status = "pending", commit_sha = "", description = "Generate baseline PNG (visual_baseline_default.png)" }
|
||||
t6_4 = { status = "pending", commit_sha = "", description = "RED test for pixel diff comparison" }
|
||||
t6_5 = { status = "pending", commit_sha = "", description = "Implement _compute_pixel_diff (numpy-based)" }
|
||||
# Phase 7
|
||||
t7_1 = { status = "pending", commit_sha = "", description = "RED test for MANUAL_SLOP_TEST_VIEWPORT env var" }
|
||||
t7_2 = { status = "pending", commit_sha = "", description = "Implement MANUAL_SLOP_TEST_VIEWPORT parsing in App.run" }
|
||||
t7_3 = { status = "pending", commit_sha = "", description = "RED test for MANUAL_SLOP_TEST_THEME env var" }
|
||||
t7_4 = { status = "pending", commit_sha = "", description = "Implement MANUAL_SLOP_TEST_THEME parsing in App.run" }
|
||||
# Phase 8
|
||||
t8_1 = { status = "pending", commit_sha = "", description = "Create scripts/check_visual_baseline.py (standalone CLI)" }
|
||||
t8_2 = { status = "pending", commit_sha = "", description = "Wire check_visual_baseline into scripts/run_tests_batched.py" }
|
||||
t8_3 = { status = "pending", commit_sha = "", description = "Write docs/guide_visual_verification.md" }
|
||||
t8_4 = { status = "pending", commit_sha = "", description = "Update conductor/tracks.md schema (VERIFIED-<date> tag requirement)" }
|
||||
t8_5 = { status = "pending", commit_sha = "", description = "Update docs/Readme.md to reference new guide" }
|
||||
# Phase 9
|
||||
t9_1 = { status = "pending", commit_sha = "", description = "Negative test: corrupted INI catches the regression (FR8)" }
|
||||
t9_2 = { status = "pending", commit_sha = "", description = "Run full test batch (scripts/run_tests_batched.py)" }
|
||||
t9_3 = { status = "pending", commit_sha = "", description = "Manual visual verification gate (user runs uv run sloppy.py)" }
|
||||
t9_4 = { status = "pending", commit_sha = "", description = "User commits VERIFIED-<date> git tag (HARD GATE)" }
|
||||
t9_5 = { status = "pending", commit_sha = "", description = "Write TRACK_COMPLETION report" }
|
||||
t9_6 = { status = "pending", commit_sha = "", description = "Update conductor/tracks.md to mark track [x]" }
|
||||
t9_7 = { status = "pending", commit_sha = "", description = "Conductor - User Manual Verification" }
|
||||
|
||||
[verification]
|
||||
phase_1_complete = false
|
||||
phase_2_complete = false
|
||||
phase_3_complete = false
|
||||
phase_4_complete = false
|
||||
phase_5_complete = false
|
||||
phase_6_complete = false
|
||||
phase_7_complete = false
|
||||
phase_8_complete = false
|
||||
phase_9_complete = false
|
||||
visual_baseline_png_committed = false
|
||||
verified_tag_exists = false
|
||||
all_tiers_pass = false
|
||||
Reference in New Issue
Block a user