From eb0bd39327e8f6bd9bd87ecebf706533fcb821bc Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 5 Jun 2026 19:24:12 -0400 Subject: [PATCH] fix(gui_2): use str sentinel not bytes in _capture_workspace_profile --- _manual_run.py | 133 +++++++++++++++++++++++++++++++++++++++++++ _repro.py | 29 ++++++++++ config.toml | 10 ++-- project_history.toml | 2 +- src/gui_2.py | 18 +++--- 5 files changed, 177 insertions(+), 15 deletions(-) create mode 100644 _manual_run.py create mode 100644 _repro.py diff --git a/_manual_run.py b/_manual_run.py new file mode 100644 index 00000000..3e78ebf0 --- /dev/null +++ b/_manual_run.py @@ -0,0 +1,133 @@ +"""Manually start sloppy.py, then run the test against the same GUI process.""" +import subprocess +import os +import sys +import time +import socket +from pathlib import Path + +# Start sloppy.py +project_root = Path("C:/projects/manual_slop").absolute() +gui_script = project_root / "sloppy.py" +test_workspace = project_root / "tests" / "artifacts" / "live_gui_workspace" + +# Clean up old workspace +if test_workspace.exists(): + import shutil + for _ in range(5): + try: + shutil.rmtree(test_workspace) + break + except PermissionError: + time.sleep(0.5) + +test_workspace.mkdir(parents=True, exist_ok=True) + +# Create minimal files +(test_workspace / "manual_slop.toml").write_text("[project]\nname = 'TestProject'\n\n[conductor]\ndir = 'conductor'\n", encoding="utf-8") +(test_workspace / "conductor" / "tracks").mkdir(parents=True, exist_ok=True) + +config_content = { + 'ai': {'provider': 'gemini', 'model': 'gemini-2.5-flash-lite'}, + 'projects': { + 'paths': [str((test_workspace / 'manual_slop.toml').absolute())], + 'active': str((test_workspace / 'manual_slop.toml').absolute()) + }, + 'paths': { + 'logs_dir': str((test_workspace / "logs").absolute()), + 'scripts_dir': str((test_workspace / "scripts" / "generated").absolute()) + }, +} +import tomli_w +with open(test_workspace / 'config.toml', 'wb') as f: + tomli_w.dump(config_content, f) + +# Start sloppy.py +os.makedirs("logs", exist_ok=True) +log_file = open("logs/sloppy_py_test_2.log", "w", encoding="utf-8") +env = os.environ.copy() +env["PYTHONPATH"] = str(project_root.absolute()) +env["SLOP_CONFIG"] = str((test_workspace / "config.toml").absolute()) +env["SLOP_GLOBAL_PRESETS"] = str((test_workspace / "presets.toml").absolute()) +env["SLOP_GLOBAL_TOOL_PRESETS"] = str((test_workspace / "tool_presets.toml").absolute()) + +print("Starting sloppy.py...") +proc = subprocess.Popen( + ["uv", "run", "python", "-u", str(gui_script), "--enable-test-hooks"], + stdout=log_file, + stderr=log_file, + text=True, + cwd=str(test_workspace.absolute()), + env=env, + creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if os.name == 'nt' else 0 +) +print(f"Started PID: {proc.pid}") + +# Wait for hook server +import requests +for i in range(30): + try: + resp = requests.get("http://127.0.0.1:8999/status", timeout=0.5) + if resp.status_code == 200: + print(f"Hook server ready after {i*0.5}s") + break + except Exception: + time.sleep(0.5) +else: + print("Hook server didn't start!") + proc.kill() + sys.exit(1) + +# Wait extra for imgui to fully initialize +print("Waiting 3s for imgui to stabilize...") +time.sleep(3.0) + +# Now run the actual test flow +from src.api_hook_client import ApiHookClient +client = ApiHookClient() + +print("\n[1] set_value show_windows {Diagnostics: True}") +client.set_value('show_windows', {'Diagnostics': True}) +time.sleep(1.0) + +print("\n[2] push_event save_workspace_profile") +client.push_event('custom_callback', {'callback': 'save_workspace_profile', 'args': ['Tier3Profile', 'project']}) +time.sleep(1.0) + +print("\n[3] set_value show_windows {Diagnostics: False}") +client.set_value('show_windows', {'Diagnostics': False}) + +print("\n[4] set_value ui_auto_switch_layout") +client.set_value('ui_auto_switch_layout', True) + +print("\n[5] set_value ui_tier_layout_bindings") +client.set_value('ui_tier_layout_bindings', {'Tier 1': '', 'Tier 2': '', 'Tier 3': 'Tier3Profile', 'Tier 4': ''}) + +def trigger_tier(tier): + client.push_event("mma_state_update", {"status": "running", "active_tier": tier}) + +print("\n[6] trigger Tier 2") +trigger_tier('Tier 2 (Tech Lead)') +time.sleep(1.0) +val = client.get_value('show_windows') +print(f"[after Tier 2] show_windows: {val!r}") +assert val is not None, "show_windows is None" +assert val.get('Diagnostics', False) == False, f"Expected False, got {val}" + +print("\n[7] trigger Tier 3") +trigger_tier('Tier 3 (Worker): task-1') +time.sleep(1.0) +val = client.get_value('show_windows') +print(f"[after Tier 3] show_windows: {val!r}") +assert val.get('Diagnostics', False) == True, f"Expected True, got {val}" + +print("\nALL ASSERTIONS PASSED!") + +# Cleanup +print("Killing sloppy.py...") +proc.kill() +try: + proc.wait(timeout=5) +except: + pass +log_file.close() diff --git a/_repro.py b/_repro.py new file mode 100644 index 00000000..da041ad8 --- /dev/null +++ b/_repro.py @@ -0,0 +1,29 @@ +"""Minimal reproducer for the auto_switch_sim GUI crash.""" +import sys +import time +import os +sys.path.insert(0, 'C:/projects/manual_slop') +sys.path.insert(0, 'C:/projects/manual_slop/src') + +from src.api_hook_client import ApiHookClient +client = ApiHookClient() +if not client.wait_for_server(timeout=15): + print('FAIL: server not up') + sys.exit(1) +print('OK: server up') + +print('Step 1: click btn_reset') +client.click('btn_reset') +time.sleep(1.0) +print('Step 1 done, status=', client.get_value('ai_status')) + +print('Step 2: set_value current_provider gemini_cli') +client.set_value('current_provider', 'gemini_cli') +time.sleep(1.0) +print('Step 2 done') + +print('Step 3: set_value gcli_path') +mock_path = os.path.abspath('tests/mock_concurrent_mma.py') +client.set_value('gcli_path', '"' + sys.executable + '" "' + mock_path + '"') +time.sleep(1.0) +print('Step 3 done') diff --git a/config.toml b/config.toml index 3346b096..359c8dee 100644 --- a/config.toml +++ b/config.toml @@ -69,6 +69,11 @@ scale = 1.0199999809265137 transparency = 1.0 child_transparency = 1.0 +[theme.tone_mapping.solarized_light] +brightness = 0.6899999976158142 +contrast = 0.8600000143051147 +gamma = 0.7699999809265137 + [theme.tone_mapping.moss] brightness = 1.059999942779541 contrast = 0.5799999833106995 @@ -84,11 +89,6 @@ brightness = 0.7699999809265137 contrast = 0.7200000286102295 gamma = 0.6899999976158142 -[theme.tone_mapping.solarized_light] -brightness = 0.6899999976158142 -contrast = 0.8600000143051147 -gamma = 0.7699999809265137 - [mma] max_workers = 4 diff --git a/project_history.toml b/project_history.toml index de6f192a..d7663da6 100644 --- a/project_history.toml +++ b/project_history.toml @@ -9,5 +9,5 @@ active = "main" [discussions.main] git_commit = "" -last_updated = "2026-06-04T20:08:39" +last_updated = "2026-06-05T18:41:59" history = [] diff --git a/src/gui_2.py b/src/gui_2.py index 3d498db3..6daa1a32 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -598,15 +598,15 @@ class App: finally: self._is_applying_snapshot = False - def _capture_workspace_profile(self, name: str) -> models.WorkspaceProfile: - if not getattr(self, "_ini_capture_ready", False): - self._ini_capture_ready = True - ini = b"" - else: - try: - ini = imgui.save_ini_settings_to_memory() - except Exception: - ini = b"" + def _capture_workspace_profile(self, name: str) -> models.WorkspaceProfile: + if not getattr(self, "_ini_capture_ready", False): + self._ini_capture_ready = True + ini = "" + else: + try: + ini = str(imgui.save_ini_settings_to_memory() or "") + except Exception: + ini = "" panel_states = { "ui_separate_context_preview": getattr(self, "ui_separate_context_preview", False), "ui_separate_message_panel": getattr(self, "ui_separate_message_panel", False),