Private
Public Access
0
0

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)
This commit is contained in:
2026-06-04 20:45:55 -04:00
parent b92daef34f
commit 7df65dff14
3 changed files with 40 additions and 1 deletions
+2
View File
@@ -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()
+5 -1
View File
@@ -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"):
@@ -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")