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.
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.inifor 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
- WHERE:
- Task 1.2:
git mvasset to new home- WHERE:
tests/artifacts/manualslop_layout_default.ini→layouts/default.ini(new dir at repo root, parallel tothemes/) - WHAT:
git mv tests/artifacts/manualslop_layout_default.ini layouts/default.ini - HOW: PowerShell
git mvpreserves history; verify withgit statusafter - SAFETY: file rename, no content change;
layouts/is gitignored? verify —grep -i "layouts" .gitignoreshould return nothing (or onlytests/artifacts/excluding layouts/)
- WHERE:
- Task 1.3: Update
tests/conftest.py:709to read fromlayouts/- 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 perconductor/code_styleguides/python.md - SAFETY: no semantic change to test behavior; same bundled content, new path
- WHERE:
- Task 1.4: Add
layoutsfield tosrc/paths.pyconfig dataclass (mirror themes)- WHERE:
src/paths.py:60(themes: Path = ...) — add alayouts: Path = ...field right after - WHAT: add the field declaration matching the
themesshape exactly - HOW:
manual-slop_edit_file; 1-space indent - SAFETY: additive — does not change existing fields
- WHERE:
- Task 1.5: Resolve
layoutsdefault insrc/paths.py(mirror themes)- WHERE:
src/paths.py:83(themes = root_dir / "themes",) — addlayouts = 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
- WHERE:
- Task 1.6: Add
SLOP_GLOBAL_LAYOUTSenv + 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_pathcall for themes - SAFETY: additive; new env var only
- WHERE:
- Task 1.7: Add
get_layouts_dir()accessor tosrc/paths.py(mirror themes)- WHERE:
src/paths.py:210-216— add 2 functions (get_layouts_dir() -> Path+get_layouts_project_config_path() -> Pathif themes has it) right after - WHAT: accessor functions
- HOW:
manual-slop_edit_file; preserve docstring format - SAFETY: additive
- WHERE:
- Task 1.8: Create
src/layouts.pyloader module (mirrorsrc/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; defineload_layouts_from_dir(path: Path, scope: str) -> dict[str, LayoutFile]andload_layouts_from_file(path: Path, scope: str) -> dict[str, LayoutFile]; defineload_layouts_from_disk() -> Nonethat calls both with global + project paths; wrap parse errors inResultperconductor/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)perconductor/code_styleguides/data_oriented_design.md§8.5
- WHERE: new file
- Task 1.9: Add
src/layouts.pytotests/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 nolayouts/) - HOW: direct Python invocation
- SAFETY: pure inspection
- WHERE:
- 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
- WHAT:
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:
test_default_layout_installed_when_ini_missing—os.remove(cwd/manualslop_layout.ini)before launch;subprocess.Popen(sloppy_args, cwd=temp_workspace); wait ≥ 5s; assertmanualslop_layout.iniexists with[Window][Project Settings]entry + a non-emptyDockId=linetest_default_layout_installed_when_ini_empty— write a 5-byte stub INI before launch; same assertions as (1)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 viakill_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 (perconductor/code_styleguides/workspace_paths.md); temp workspace path =Path("tests/artifacts/_default_layout_install_<pid>")
- WHERE: new file
- 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
- WHERE:
- Task 2.3: Implement
_install_default_layout_if_emptyhelper- 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_initext, decides ifdst_iniis "missing/empty" (file size < 1000 bytes OR zero[Window][lines), copies bundled → dst on true, returns Result[True]; on false returns Result[False]; onOSErrorreturns Result with ErrorInfo perconductor/code_styleguides/error_handling.md - HOW:
shutil.copy2for 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
- WHERE: new module-level function
- Task 2.4: Wire the helper into
App._post_init- WHERE:
src/gui_2.py:570-582(App._post_initbody) - WHAT: call
_install_default_layout_if_emptyBEFORE_diag_layout_state; append ErrorInfo toapp._startup_timeline_errorsifnot result.ok - HOW:
install_result = _install_default_layout_if_empty_result(app, src_path, dst_path); if not ok, drain via_startup_timeline_errorsper the existing pattern at line 580-582 - SAFETY:
_post_initruns on the main thread (HelloImGui callback), no race
- WHERE:
- Task 2.5: Add drain helper
_install_default_layout_if_empty_result- WHERE:
src/gui_2.pynear other drain helpers (line 1448 area:_post_init_callback_result) - WHAT:
Result[None]wrapper for the install; mirrors the existingResult-returning pattern for_post_init_callback_resultand_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
- WHERE:
- 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
- WHERE:
- 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*(perconductor/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
- WHERE:
- 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_errorsper data-oriented convention."
- WHAT:
- Task 2.9: User Manual Verification
- PAUSE; await user
yesafter deletingmanualslop_layout.iniand launchingsloppy.pystandalone — confirm panels are visible
- PAUSE; await user
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_layoutpath cleanup- WHERE: new file
tests/test_reset_layout.py - WHAT: red phase — verify
reset_layoutonly consults the cwd-relative pathtest_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); invokereset_layout(app)on a mock app withshow_windows = {}; useinspect.getsource(commands.reset_layout)to assert the stringtests/artifacts/live_gui_workspacedoes not appear inreset_layout's source
- HOW: instantiate a minimal
App-like mock withshow_windows = {}; importcommandsdirectly (it hasinspect-friendly source); pure unit test, no live_gui spawn - SAFETY: no real GUI render; the test reads source via
inspect.getsource()
- WHERE: new file
- 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_layoutsource containstests/artifacts/live_gui_workspace(the hardcoded path the user flagged)
- HOW:
- Task 3.3: Remove the hardcoded path from
commands.reset_layout- WHERE:
src/commands.py:369-376 - WHAT:
layout_paths = ["manualslop_layout.ini"](drop theos.path.join("tests", ...)line) - HOW:
manual-slop_edit_filewithold_stringcontaining bothlayout_paths = [and theos.path.join(...)line; replace withlayout_paths = ["manualslop_layout.ini"] - SAFETY: shrinks the function; no behavior change for end users (cwd-relative was the only functional path)
- WHERE:
- Task 3.4: Update
commands.reset_layoutdocstring- 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
- WHERE:
- 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.getsourceassertion holds
- HOW:
- 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
- HOW:
- 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."
- WHAT:
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_missingpasses - G2 (overrides cleared on install) —
test_default_layout_installed_when_ini_emptypasses - G3 (
reset_layoutpath cleanup) —test_reset_layout_only_targets_cwd_inipasses - G4 (regression test for visibility-after-empty) — all 3
test_default_layout_installtests pass - G5 / G6 / G7 (layouts/ stack) —
tests/conftest.py:709reads from new path; live_gui fixture unaffected;src/layouts.pyimportable - G8 (conftest path update) —
tests/conftest.py:709reads fromlayouts/default.ini
- G1 (install on empty INI) —
- WHERE:
- 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.ini→layouts/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.pyaddslayoutsfield), 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
- WHAT:
- 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
- WHAT:
- Task 4.6: Add row to
conductor/tracks.md- WHERE:
conductor/tracks.md— Active Tracks table - WHAT: add a row for
default_layout_install_20260629with 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
- WHERE: