Files
manual_slop/conductor/tracks/default_layout_install_20260629/plan.md
T
ed 5ad062b13a conductor(track): init default_layout_install_20260629 (empty INI -> install default; layouts/ at root + src/layouts.py; reset_layout path cleanup)
Bug: when cwd/manualslop_layout.ini is missing/empty after first-run,
post-deletion, or post-corrupt-INI, the GUI panels are not visible
despite show_windows[name] = True. Root cause is structural: imgui.begin
without [Window][name] + DockId in the INI produces a floating window
that gets clipped by the full-screen dockspace. Empirically confirmed:
8s of running produces a 585-byte INI containing only [Window][Debug##Default].

Fix shape (4 phases):
  Phase 1: relocate tests/artifacts/manualslop_layout_default.ini ->
           layouts/default.ini (at repo root, parallel to themes/ per
           user directive 'no configs in src/'); add src/paths.py
           'layouts' field + SLOP_GLOBAL_LAYOUTS env override (mirror
           themes pattern at line 60/83/150/210-216); add src/layouts.py
           loader module (mirror src/theme_models.py + src/theme_2.py
           contract; LayoutFile = @dataclass(frozen=True, slots=True)
           per the C11/Odin/Jai-in-Python value-type mandate).
  Phase 2: install-on-empty-INI in App._post_init. _install_default_layout_if_empty
           helper + drain helper, called BEFORE _diag_layout_state and
           BEFORE immapp.run. logs '[GUI] installed default layout: <src> -> <dst>'.
  Phase 3: drop hardcoded 'tests/artifacts/live_gui_workspace/...' path
           from src/commands.py:reset_layout line 369-376 (dead code in
           production; violates 'production code defaults to immediate
           directory' directive 2026-06-29).
  Phase 4: 3-test regression suite in tests/test_default_layout_install.py
           + 1 unit test in tests/test_reset_layout.py; user manual verify
           (delete INI, run sloppy.py standalone, see panels).

TDD red-first per task. Atomic per-task commits with git notes (per
conductor/workflow.md §Task Workflow step 9-10). No day estimates per
conductor/workflow.md §Tier 1 Track Initialization Rules.

Out of scope (deferred): panel_defs_fleury_migration - migrate the ~40
render_x functions to declarative PanelDef records per Ryan Fleury's
raddbg 'type view' / 'lens' pattern. Spec §Eventual Normalization Target
documents the design sketch + the transcripts at docs/transcripts/.
This track sets up layouts/ at repo root + src/layouts.py as the typed
loader so the future migration has somewhere to land.

Tracks.md row will be added in Phase 4 (Task 4.6) when the track ships.
2026-06-29 14:02:41 -04:00

16 KiB

Phase 1: Move default layout + create layouts/ stack (parallel to themes/)

Focus: relocate tests/artifacts/manualslop_layout_default.ini to layouts/default.ini at repo root; add the parallel src/paths.py field, get_layouts_dir() accessor, and src/layouts.py loader module — exactly the themes pattern (themes/ + src/path.py:60,83,150 + src/theme_models.py + src/theme_2.py).

  • Task 1.1: Verify bundled layout content + themes pattern baseline
    • WHERE: tests/artifacts/manualslop_layout_default.ini (109 lines), src/paths.py:60,83,150,210-216, src/theme_models.py:181-225, src/theme_2.py:340-346, themes/ at repo root
    • WHAT: confirm files exist with the expected sizes and that the themes pattern is the canonical reference
    • HOW: git log --oneline -- tests/artifacts/manualslop_layout_default.ini for provenance; Get-Content src/paths.py | Select-String -Pattern "themes\s*[:=]|themes\s*=|root_dir/" to map the path-resolution shape
    • SAFETY: pure read; no behavior change
  • Task 1.2: git mv asset to new home
    • WHERE: tests/artifacts/manualslop_layout_default.inilayouts/default.ini (new dir at repo root, parallel to themes/)
    • WHAT: git mv tests/artifacts/manualslop_layout_default.ini layouts/default.ini
    • HOW: PowerShell git mv preserves history; verify with git status after
    • SAFETY: file rename, no content change; layouts/ is gitignored? verify — grep -i "layouts" .gitignore should return nothing (or only tests/artifacts/ excluding layouts/)
  • Task 1.3: Update tests/conftest.py:709 to read from layouts/
    • WHERE: tests/conftest.py:709_default_layout_src = project_root / "tests" / "artifacts" / "manualslop_layout_default.ini"
    • WHAT: change to _default_layout_src = project_root / "layouts" / "default.ini"
    • HOW: manual-slop_edit_file; preserve 1-space indentation per conductor/code_styleguides/python.md
    • SAFETY: no semantic change to test behavior; same bundled content, new path
  • Task 1.4: Add layouts field to src/paths.py config dataclass (mirror themes)
    • WHERE: src/paths.py:60 (themes: Path = ...) — add a layouts: Path = ... field right after
    • WHAT: add the field declaration matching the themes shape exactly
    • HOW: manual-slop_edit_file; 1-space indent
    • SAFETY: additive — does not change existing fields
  • Task 1.5: Resolve layouts default in src/paths.py (mirror themes)
    • WHERE: src/paths.py:83 (themes = root_dir / "themes",) — add layouts = root_dir / "layouts", immediately below
    • WHAT: resolve the default path in the initialize_paths-style function
    • HOW: manual-slop_edit_file; ensure the same closure/call-site shape as themes
    • SAFETY: additive; existing themes path unchanged
  • Task 1.6: Add SLOP_GLOBAL_LAYOUTS env + config override (mirror themes)
    • WHERE: src/paths.py:150 — add _resolve_path("SLOP_GLOBAL_LAYOUTS", "layouts", root_dir / "layouts", config_path) line in the same call shape
    • WHAT: register the env var + config-file override for layouts, parallel to themes
    • HOW: manual-slop_edit_file; exact-string preserve the existing _resolve_path call for themes
    • SAFETY: additive; new env var only
  • Task 1.7: Add get_layouts_dir() accessor to src/paths.py (mirror themes)
    • WHERE: src/paths.py:210-216 — add 2 functions (get_layouts_dir() -> Path + get_layouts_project_config_path() -> Path if themes has it) right after
    • WHAT: accessor functions
    • HOW: manual-slop_edit_file; preserve docstring format
    • SAFETY: additive
  • Task 1.8: Create src/layouts.py loader module (mirror src/theme_models.py + src/theme_2.py)
    • WHERE: new file src/layouts.py
    • WHAT: define LayoutFile @dataclass(frozen=True, slots=True) with (name: str, raw_text: str, source_path: Path, scope: str) fields; define load_layouts_from_dir(path: Path, scope: str) -> dict[str, LayoutFile] and load_layouts_from_file(path: Path, scope: str) -> dict[str, LayoutFile]; define load_layouts_from_disk() -> None that calls both with global + project paths; wrap parse errors in Result per conductor/code_styleguides/error_handling.md
    • HOW: model after src/theme_models.py:181-225 (load_themes_from_dir, load_themes_from_toml) + src/theme_2.py:340-346 (load_themes_from_disk)
    • SAFETY: new file, no existing code modification; uses from __future__ import annotations + @dataclass(frozen=True, slots=True) per conductor/code_styleguides/data_oriented_design.md §8.5
  • Task 1.9: Add src/layouts.py to tests/test_gui2_layout.py-adjacent test inventory if present; for now, just verify import works
    • WHERE: tests/
    • WHAT: uv run python -c "from src.layouts import load_layouts_from_disk; print(load_layouts_from_disk())" to verify the module imports and returns a dict (empty by default since the test cwd has no layouts/)
    • HOW: direct Python invocation
    • SAFETY: pure inspection
  • Task 1.10: Commit phase 1 with git note
    • WHAT: chore(layouts): introduce layouts/ directory + src/layouts.py (themes pattern); relocate default layout asset
    • HOW: standard atomic commit per conductor/workflow.md §Task Workflow; attach a 3-line git note explaining: relocation from tests/artifacts; parallel to themes; src/layouts.py mirrors src/theme_models.py + src/theme_2.py; sets up the home for eventual Fleury-style PanelDef migration

Phase 2: Install-on-empty-INI in App._post_init

Focus: ship layouts/default.ini to cwd/manualslop_layout.ini when the file is missing/empty/small, before immapp.run(...) reads it.

  • Task 2.1: Write failing test for install behavior
    • WHERE: new file tests/test_default_layout_install.py
    • WHAT: red phase — 3 tests:
      1. test_default_layout_installed_when_ini_missingos.remove(cwd/manualslop_layout.ini) before launch; subprocess.Popen(sloppy_args, cwd=temp_workspace); wait ≥ 5s; assert manualslop_layout.ini exists with [Window][Project Settings] entry + a non-empty DockId= line
      2. test_default_layout_installed_when_ini_empty — write a 5-byte stub INI before launch; same assertions as (1)
      3. test_default_layout_NOT_installed_when_layout_present — pre-write a custom [Window][CustomPanel] INI; assert the custom panel survives (no overwrite)
    • HOW: each test spawns the app via subprocess.Popen(["uv", "run", "python", "-u", "sloppy.py", "--enable-test-hooks"], cwd=temp_workspace, stdout=log_file, stderr=log_file, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) (mirrors the conftest at line 792), waits 5-8s, terminates via kill_process_tree() (per the conftest pattern at line 853), then asserts on the saved INI
    • SAFETY: tests MUST NOT touch the repo-root manualslop_layout.ini; each test uses its own cwd (per conductor/code_styleguides/workspace_paths.md); temp workspace path = Path("tests/artifacts/_default_layout_install_<pid>")
  • Task 2.2: Run phase 2.1 tests; confirm RED (fails for the right reason)
    • WHERE: tests/test_default_layout_install.py
    • HOW: uv run pytest tests/test_default_layout_install.py -v --tb=short --timeout=120
    • Expected: 3 tests fail because no install logic exists yet; the temp-workspace INI is empty or absent post-launch
  • Task 2.3: Implement _install_default_layout_if_empty helper
    • WHERE: new module-level function _install_default_layout_if_empty(src_ini: Path, dst_ini: Path) -> Result[bool] near _diag_layout_state (src/gui_2.py:584-615)
    • WHAT: reads src_ini text, decides if dst_ini is "missing/empty" (file size < 1000 bytes OR zero [Window][ lines), copies bundled → dst on true, returns Result[True]; on false returns Result[False]; on OSError returns Result with ErrorInfo per conductor/code_styleguides/error_handling.md
    • HOW: shutil.copy2 for atomic copy; sys.stderr.write(f"[GUI] installed default layout: {src_ini} -> {dst_ini}\n") for the user-visible log
    • SAFETY: thread-safe (no shared state); pure file I/O; 1-space indentation per project rule
  • Task 2.4: Wire the helper into App._post_init
    • WHERE: src/gui_2.py:570-582 (App._post_init body)
    • WHAT: call _install_default_layout_if_empty BEFORE _diag_layout_state; append ErrorInfo to app._startup_timeline_errors if not result.ok
    • HOW: install_result = _install_default_layout_if_empty_result(app, src_path, dst_path); if not ok, drain via _startup_timeline_errors per the existing pattern at line 580-582
    • SAFETY: _post_init runs on the main thread (HelloImGui callback), no race
  • Task 2.5: Add drain helper _install_default_layout_if_empty_result
    • WHERE: src/gui_2.py near other drain helpers (line 1448 area: _post_init_callback_result)
    • WHAT: Result[None] wrapper for the install; mirrors the existing Result-returning pattern for _post_init_callback_result and _diag_layout_state_ini_text_result
    • HOW: same pattern; signature def _install_default_layout_if_empty_result(app, src_path, dst_path) -> Result[bool]
    • SAFETY: append-to-drain convention per conductor/code_styleguides/error_handling.md
  • Task 2.6: Verify phase 2.1 tests now pass
    • WHERE: tests/test_default_layout_install.py
    • HOW: uv run pytest tests/test_default_layout_install.py -v --tb=short --timeout=120
    • Expected: all 3 pass; the post-launch INI has 7+ [Window][X] entries
  • Task 2.7: Run adjacent test batch (tests/test_gui*.py) to confirm no regression
    • WHERE: tests/test_gui2_layout.py, tests/test_gui_diagnostics.py, tests/test_layout_reorganization.py
    • HOW: uv run python scripts/run_tests_batched.py --tier test_gui* (per conductor/workflow.md §"Tier 2 Autonomous Sandbox" — use the batched runner, never raw pytest for batched verification)
    • Expected: prior batch_green preserved; no _post_init regressions
  • Task 2.8: Commit phase 2 with git note
    • WHAT: fix(gui): install default layout when cwd/manualslop_layout.ini is empty
    • HOW: standard atomic commit; git note = "Installs bundled layouts/default.ini (resolved via the new src/layouts.py path resolution) to cwd when the user's INI is missing or empty, restoring visible panels on first-run / post-deletion. Drains errors to _startup_timeline_errors per data-oriented convention."
  • Task 2.9: User Manual Verification
    • PAUSE; await user yes after deleting manualslop_layout.ini and launching sloppy.py standalone — confirm panels are visible

Phase 3: Remove hardcoded test-fixture path from production code

Focus: src/commands.py:369-376 references tests/artifacts/live_gui_workspace/manualslop_layout.ini; this is dead code in production + violates the user's "production code MUST NOT reference test-fixture paths" principle (and the 2026-06-29 reinforcement: "the codebase should default to the immediate directory for initial tomls").

  • Task 3.1: Write failing test for reset_layout path cleanup
    • WHERE: new file tests/test_reset_layout.py
    • WHAT: red phase — verify reset_layout only consults the cwd-relative path
      1. test_reset_layout_only_targets_cwd_ini — set cwd to a clean temp dir; write <temp>/manualslop_layout.ini; create <temp>/tests/artifacts/live_gui_workspace/manualslop_layout.ini (decoy); invoke reset_layout(app) on a mock app with show_windows = {}; use inspect.getsource(commands.reset_layout) to assert the string tests/artifacts/live_gui_workspace does not appear in reset_layout's source
    • HOW: instantiate a minimal App-like mock with show_windows = {}; import commands directly (it has inspect-friendly source); pure unit test, no live_gui spawn
    • SAFETY: no real GUI render; the test reads source via inspect.getsource()
  • Task 3.2: Run phase 3.1 tests; confirm RED
    • HOW: uv run pytest tests/test_reset_layout.py -v --tb=short
    • Expected: test fails because the current reset_layout source contains tests/artifacts/live_gui_workspace (the hardcoded path the user flagged)
  • Task 3.3: Remove the hardcoded path from commands.reset_layout
    • WHERE: src/commands.py:369-376
    • WHAT: layout_paths = ["manualslop_layout.ini"] (drop the os.path.join("tests", ...) line)
    • HOW: manual-slop_edit_file with old_string containing both layout_paths = [ and the os.path.join(...) line; replace with layout_paths = ["manualslop_layout.ini"]
    • SAFETY: shrinks the function; no behavior change for end users (cwd-relative was the only functional path)
  • Task 3.4: Update commands.reset_layout docstring
    • WHERE: src/commands.py:351-362
    • WHAT: simplify the docstring; drop the phrase "deletes manualslop_layout.ini so hello_imgui regenerates a fresh" if no longer accurate
    • HOW: minimal edit via manual-slop_edit_file
    • SAFETY: docstring only, no behavior change
  • Task 3.5: Verify phase 3.1 tests now pass
    • HOW: uv run pytest tests/test_reset_layout.py -v --tb=short
    • Expected: 1 test passes; the inspect.getsource assertion holds
  • Task 3.6: Run adjacent test_batch (tests/test_commands*.py)
    • HOW: uv run python scripts/run_tests_batched.py --filter test_commands*
    • Expected: no regression
  • Task 3.7: Commit phase 3 with git note
    • WHAT: chore(commands): remove dead test-fixture path from reset_layout
    • HOW: standard atomic commit; git note = "Reset_layout referenced tests/artifacts/live_gui_workspace/manualslop_layout.ini, dead code in production. Removed per user directive 2026-06-29: production code must default to the immediate directory."

Phase 4: Verification

Focus: full-batch confirmation; per-target test runs; cross-reference the original bug report.

  • Task 4.1: Confirm spec acceptance criteria via test execution
    • WHERE: tests/test_default_layout_install.py, tests/test_reset_layout.py, tests/test_gui*.py, tests/test_commands*.py
    • HOW: uv run python scripts/run_tests_batched.py --filter "test_default_layout_install|test_reset_layout|test_gui2_layout|test_gui_diagnostics|test_layout_reorganization|test_commands"
    • Acceptance:
      • G1 (install on empty INI) — test_default_layout_installed_when_ini_missing passes
      • G2 (overrides cleared on install) — test_default_layout_installed_when_ini_empty passes
      • G3 (reset_layout path cleanup) — test_reset_layout_only_targets_cwd_ini passes
      • G4 (regression test for visibility-after-empty) — all 3 test_default_layout_install tests pass
      • G5 / G6 / G7 (layouts/ stack) — tests/conftest.py:709 reads from new path; live_gui fixture unaffected; src/layouts.py importable
      • G8 (conftest path update) — tests/conftest.py:709 reads from layouts/default.ini
  • Task 4.2: Empirical reproduction of the original bug
    • WHERE: production cwd (no test harness)
    • HOW: Remove-Item manualslop_layout.ini -ErrorAction SilentlyContinue; uv run python sloppy.py; observe via screenshot or VNC that Project Settings, Files & Media, AI Settings, Discussion Hub, Operations Hub, Theme, Log Management, Diagnostics are all visible
    • SAFETY: requires user confirmation; this is the manual verification step per conductor/workflow.md §"Phase Completion Verification"
  • Task 4.3: Checkpoint commit + verification git note
    • WHAT: conductor(checkpoint): end of Phase 4 (default_layout_install_20260629 complete)
    • HOW: empty commit allowed per conductor/workflow.md §"Phase Completion Verification"; attach a long-form verification report as git note documenting the 1 asset relocated (tests/artifacts/manualslop_layout_default.inilayouts/default.ini), 2 new files (layouts/default.ini, src/layouts.py), 1 src command file modified (commands.reset_layout), 1 paths file modified (src/paths.py adds layouts field), 1 conftest updated (tests/conftest.py:709), 3 test files added (tests/test_default_layout_install.py, tests/test_reset_layout.py, etc.), and the empirical reproduction result
  • Task 4.4: Append phase checkpoint + completion SHAs to plan.md
    • WHERE: this file
    • WHAT: append [checkpoint: <7-char SHA>] after each phase header + [track complete: <7-char SHA>] at end
  • Task 4.5: Commit final plan update
    • WHAT: conductor(plan): mark default_layout_install_20260629 phases 1-4 complete
  • Task 4.6: Add row to conductor/tracks.md
    • WHERE: conductor/tracks.md — Active Tracks table
    • WHAT: add a row for default_layout_install_20260629 with status "shipped 2026-06-29" after Phase 4 completes
    • HOW: insert a new row following the table conventions; commit in the same Phase 4 commit batch