From 7df65dff1478730a711ecbfac966482171dc9c46 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 4 Jun 2026 20:45:55 -0400 Subject: [PATCH] fix(project): Create persona_manager in _load_active_project + handle missing context preset Two fixes for the regression introduced in b92daef3 (and an additional hardening for the persona->context_preset stale-reference class of bug): 1. Regression: persona_manager was missing on first project load. _load_active_project creates preset_manager and tool_preset_manager but did not create persona_manager, so the new self.personas = self.persona_manager.load_all() line in _refresh_from_project raised AttributeError on app startup before the post-_load_active_project persona_manager creation could run. Fix: create self.persona_manager in _load_active_project alongside the other managers, so the manager is available when _refresh_from_project runs. 2. Stale reference: persona's context_preset field pointed to a preset (e.g. 'GTE') that no longer exists in the project, causing load_context_preset to raise KeyError and crash the persona selector panel (which triggered the cascading 'Missing End()' imgui assertion). Fix: wrap the load_context_preset call in render_persona_selector_panel with try/except KeyError, surface the error in app.ai_status, and clear app.ui_active_context_preset to keep the GUI state consistent. Tests: 2 new tests in tests/test_project_switch_persona_preset.py - test_load_active_project_creates_persona_manager (regression guard) - test_load_context_preset_missing_raises_keyerror (verifies the contract that load_context_preset raises for missing names; the GUI layer is now responsible for catching the error) --- src/app_controller.py | 2 ++ src/gui_2.py | 6 +++- tests/test_project_switch_persona_preset.py | 33 +++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/app_controller.py b/src/app_controller.py index a1bf4c46..6f8e39e7 100644 --- a/src/app_controller.py +++ b/src/app_controller.py @@ -2009,6 +2009,8 @@ class AppController: self.project_paths.append(fallback_path) self.preset_manager = presets.PresetManager(Path(self.active_project_path).parent if self.active_project_path else None) self.tool_preset_manager = tool_presets.ToolPresetManager(Path(self.active_project_path).parent if self.active_project_path else None) + from src.personas import PersonaManager + self.persona_manager = PersonaManager(Path(self.active_project_path).parent if self.active_project_path else None) self._refresh_from_project() self._configure_mcp_for_project() diff --git a/src/gui_2.py b/src/gui_2.py index 97566ff4..19e1800e 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -2140,7 +2140,11 @@ def render_persona_selector_panel(app: App) -> None: ai_client.set_bias_profile(persona.bias_profile) if getattr(persona, 'context_preset', None): app.ui_active_context_preset = persona.context_preset - app.load_context_preset(persona.context_preset) + try: + app.load_context_preset(persona.context_preset) + except KeyError as e: + app.ai_status = f"persona context preset missing: {e}" + app.ui_active_context_preset = "" imgui.end_combo() imgui.same_line() if imgui.button("Manage Personas"): diff --git a/tests/test_project_switch_persona_preset.py b/tests/test_project_switch_persona_preset.py index 6a93e095..d6f14da0 100644 --- a/tests/test_project_switch_persona_preset.py +++ b/tests/test_project_switch_persona_preset.py @@ -137,3 +137,36 @@ def test_switch_project_preserves_global_preset(tmp_path, monkeypatch): ctrl._switch_project(str(project_b_path)) assert ctrl.ui_global_preset_name == original_global + + +def test_load_active_project_creates_persona_manager(tmp_path, monkeypatch): + project_a_path, _ = _setup_two_projects(tmp_path) + + ctrl = AppController() + monkeypatch.setattr(ctrl, "_rebuild_rag_index", lambda: None) + monkeypatch.setattr(ctrl, "_flush_to_project", lambda: None) + monkeypatch.setattr(ctrl, "_configure_mcp_for_project", lambda: None) + + assert not hasattr(ctrl, "persona_manager") + + ctrl.active_project_path = str(project_a_path) + ctrl._load_active_project() + + assert hasattr(ctrl, "persona_manager") + assert "PersonaA" in ctrl.personas + assert ctrl.ui_active_bias_profile is None or ctrl.ui_active_bias_profile in ctrl.bias_profiles + + +def test_load_context_preset_missing_raises_keyerror(tmp_path, monkeypatch): + project_a_path, _ = _setup_two_projects(tmp_path) + + ctrl = AppController() + monkeypatch.setattr(ctrl, "_rebuild_rag_index", lambda: None) + monkeypatch.setattr(ctrl, "_flush_to_project", lambda: None) + monkeypatch.setattr(ctrl, "_configure_mcp_for_project", lambda: None) + + ctrl.active_project_path = str(project_a_path) + ctrl._load_active_project() + + with pytest.raises(KeyError, match="Context preset 'NonexistentPreset' not found"): + ctrl.load_context_preset("NonexistentPreset")