refactor(controller): Replace legacy _set_status calls with direct property assignment
This commit is contained in:
+49
-56
@@ -671,13 +671,7 @@ class AppController:
|
||||
else:
|
||||
ai_client._gemini_cli_adapter.binary_path = str(path)
|
||||
|
||||
def _set_status(self, status: str) -> None:
|
||||
"""Thread-safe update of ai_status via the GUI task queue."""
|
||||
self.ai_status = status
|
||||
|
||||
def _set_mma_status(self, status: str) -> None:
|
||||
"""Thread-safe update of mma_status via the GUI task queue."""
|
||||
self.mma_status = status
|
||||
|
||||
def _set_rag_status(self, status: str) -> None:
|
||||
"""Thread-safe update of rag_status via the GUI task queue."""
|
||||
@@ -1236,7 +1230,7 @@ class AppController:
|
||||
session_dir = log_path.parent
|
||||
|
||||
if not log_file.exists():
|
||||
self._set_status(f"log file not found: {log_file}")
|
||||
self.ai_status = f"log file not found: {log_file}"
|
||||
return
|
||||
|
||||
def _resolve_log_ref(content: Any, session_dir: Path) -> str:
|
||||
@@ -1376,7 +1370,7 @@ class AppController:
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
except Exception as e:
|
||||
self._set_status(f"log load error: {e}")
|
||||
self.ai_status = f"log load error: {e}"
|
||||
return
|
||||
|
||||
self.session_usage = new_usage
|
||||
@@ -1395,7 +1389,7 @@ class AppController:
|
||||
self.prior_tool_calls = final_tool_calls
|
||||
self.is_viewing_prior_session = True
|
||||
self._trigger_gui_refresh()
|
||||
self._set_status(f"viewing prior session: {session_dir.name} ({len(entries)} entries)")
|
||||
self.ai_status = f"viewing prior session: {session_dir.name} ({len(entries)} entries)"
|
||||
|
||||
|
||||
def cb_exit_prior_session(self):
|
||||
@@ -1418,11 +1412,11 @@ class AppController:
|
||||
self.prior_disc_entries.clear()
|
||||
self.prior_tool_calls.clear()
|
||||
self._trigger_gui_refresh()
|
||||
self._set_status('idle')
|
||||
self.ai_status = 'idle'
|
||||
|
||||
def cb_prune_logs(self) -> None:
|
||||
"""Manually triggers the log pruning process with aggressive thresholds."""
|
||||
self._set_status("Manual prune started (Age > 0d, Size < 100KB)...")
|
||||
self.ai_status = "Manual prune started (Age > 0d, Size < 100KB)..."
|
||||
|
||||
def run_manual_prune() -> None:
|
||||
try:
|
||||
@@ -1433,9 +1427,9 @@ class AppController:
|
||||
# Aggressive: Prune anything not whitelisted, even if just created, if under 100KB
|
||||
# Note: max_age_days=0 means cutoff is NOW.
|
||||
pruner.prune(max_age_days=0, min_size_kb=100)
|
||||
self._set_status("Manual prune complete.")
|
||||
self.ai_status = "Manual prune complete."
|
||||
except Exception as e:
|
||||
self._set_status(f"Manual prune error: {e}")
|
||||
self.ai_status = f"Manual prune error: {e}"
|
||||
print(f"Error during manual log pruning: {e}")
|
||||
|
||||
thread = threading.Thread(target=run_manual_prune, daemon=True)
|
||||
@@ -1481,7 +1475,7 @@ class AppController:
|
||||
thread.start()
|
||||
|
||||
def _fetch_models(self, provider: str) -> None:
|
||||
self._set_status("fetching models...")
|
||||
self.ai_status = "fetching models..."
|
||||
|
||||
def do_fetch() -> None:
|
||||
try:
|
||||
@@ -1497,9 +1491,9 @@ class AppController:
|
||||
if self.current_model not in models_list and models_list:
|
||||
self.current_model = models_list[0]
|
||||
ai_client.set_provider(self._current_provider, self.current_model)
|
||||
self._set_status(f"models loaded: {len(models_list)}")
|
||||
self.ai_status = f"models loaded: {len(models_list)}"
|
||||
except Exception as e:
|
||||
self._set_status(f"model fetch error: {e}")
|
||||
self.ai_status = f"model fetch error: {e}"
|
||||
self.models_thread = threading.Thread(target=do_fetch, daemon=True)
|
||||
self.models_thread.start()
|
||||
|
||||
@@ -1611,7 +1605,7 @@ class AppController:
|
||||
|
||||
def _handle_request_event(self, event: events.UserRequestEvent) -> None:
|
||||
"""Processes a UserRequestEvent by calling the AI client."""
|
||||
self._set_status('sending...')
|
||||
self.ai_status = 'sending...'
|
||||
ai_client.set_current_tier(None) # Ensure main discussion is untagged
|
||||
# Clear response area for new turn
|
||||
self.ai_response = ""
|
||||
@@ -1785,9 +1779,9 @@ class AppController:
|
||||
|
||||
def _confirm_and_run(self, script: str, base_dir: str, qa_callback: Optional[Callable[[str], str]] = None, patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> Optional[str]:
|
||||
if self.test_hooks_enabled and not getattr(self, "ui_manual_approve", False):
|
||||
self._set_status("running powershell...")
|
||||
self.ai_status = "running powershell..."
|
||||
output = shell_runner.run_powershell(script, base_dir, qa_callback=qa_callback, patch_callback=patch_callback)
|
||||
self._set_status("powershell done, awaiting AI...")
|
||||
self.ai_status = "powershell done, awaiting AI..."
|
||||
return output
|
||||
dialog = ConfirmDialog(script, base_dir)
|
||||
is_headless = "--headless" in sys.argv
|
||||
@@ -1814,10 +1808,10 @@ class AppController:
|
||||
if not approved:
|
||||
self._append_tool_log(final_script, "REJECTED by user")
|
||||
return None
|
||||
self._set_status("running powershell...")
|
||||
self.ai_status = "running powershell..."
|
||||
output = shell_runner.run_powershell(final_script, base_dir, qa_callback=qa_callback, patch_callback=patch_callback)
|
||||
self._append_tool_log(final_script, output)
|
||||
self._set_status("powershell done, awaiting AI...")
|
||||
self.ai_status = "powershell done, awaiting AI..."
|
||||
return output
|
||||
|
||||
def _append_tool_log(self, script: str, result: str, source_tier: str | None = None, elapsed_ms: float = 0.0) -> None:
|
||||
@@ -2165,7 +2159,7 @@ class AppController:
|
||||
self._flush_to_project()
|
||||
self._flush_to_config()
|
||||
models.save_config(self.config)
|
||||
self._set_status("config saved")
|
||||
self.ai_status = "config saved"
|
||||
|
||||
def _cb_reset_base_prompt(self, user_data=None) -> None:
|
||||
self.ui_base_system_prompt = ai_client._SYSTEM_PROMPT
|
||||
@@ -2187,7 +2181,7 @@ class AppController:
|
||||
|
||||
def _switch_project(self, path: str) -> None:
|
||||
if not Path(path).exists():
|
||||
self._set_status(f"project file not found: {path}")
|
||||
self.ai_status = f"project file not found: {path}"
|
||||
return
|
||||
self._flush_to_project()
|
||||
try:
|
||||
@@ -2199,10 +2193,10 @@ class AppController:
|
||||
from src.personas import PersonaManager
|
||||
self.persona_manager = PersonaManager(new_root)
|
||||
except Exception as e:
|
||||
self._set_status(f"failed to load project: {e}")
|
||||
self.ai_status = f"failed to load project: {e}"
|
||||
return
|
||||
self._refresh_from_project()
|
||||
self._set_status(f"switched to: {Path(path).stem}")
|
||||
self.ai_status = f"switched to: {Path(path).stem}"
|
||||
|
||||
def _refresh_from_project(self) -> None:
|
||||
# Deserialize FileItems in files.paths
|
||||
@@ -2391,9 +2385,9 @@ class AppController:
|
||||
else:
|
||||
self.disc_entries = []
|
||||
self._recalculate_session_usage()
|
||||
self._set_status(f"Loaded track: {state.metadata.name}")
|
||||
self.ai_status = f"Loaded track: {state.metadata.name}"
|
||||
except Exception as e:
|
||||
self._set_status(f"Load track error: {e}")
|
||||
self.ai_status = f"Load track error: {e}"
|
||||
print(f"Error loading track {track_id}: {e}")
|
||||
|
||||
def _save_active_project(self) -> None:
|
||||
@@ -2402,7 +2396,7 @@ class AppController:
|
||||
cleaned = project_manager.clean_nones(self.project)
|
||||
project_manager.save_project(cleaned, self.active_project_path)
|
||||
except Exception as e:
|
||||
self._set_status(f"save error: {e}")
|
||||
self.ai_status = f"save error: {e}"
|
||||
|
||||
def _get_discussion_names(self) -> list[str]:
|
||||
disc_sec = self.project.get("discussion", {})
|
||||
@@ -2414,7 +2408,7 @@ class AppController:
|
||||
disc_sec = self.project.get("discussion", {})
|
||||
discussions = disc_sec.get("discussions", {})
|
||||
if name not in discussions:
|
||||
self._set_status(f"discussion not found: {name}")
|
||||
self.ai_status = f"discussion not found: {name}"
|
||||
return
|
||||
self.active_discussion = name
|
||||
self._track_discussion_active = False
|
||||
@@ -2422,7 +2416,7 @@ class AppController:
|
||||
disc_data = discussions[name]
|
||||
with self._disc_entries_lock:
|
||||
self.disc_entries = models.parse_history_entries(disc_data.get("history", []), self.disc_roles)
|
||||
self._set_status(f"discussion: {name}")
|
||||
self.ai_status = f"discussion: {name}"
|
||||
|
||||
def _flush_disc_entries_to_project(self) -> None:
|
||||
history_strings = [project_manager.entry_to_str(e) for e in self.disc_entries]
|
||||
@@ -2439,7 +2433,7 @@ class AppController:
|
||||
disc_sec = self.project.setdefault("discussion", {})
|
||||
discussions = disc_sec.setdefault("discussions", {})
|
||||
if name in discussions:
|
||||
self._set_status(f"discussion '{name}' already exists")
|
||||
self.ai_status = f"discussion '{name}' already exists"
|
||||
return
|
||||
discussions[name] = project_manager.default_discussion()
|
||||
self._switch_discussion(name)
|
||||
@@ -2464,7 +2458,7 @@ class AppController:
|
||||
if old_name not in discussions:
|
||||
return
|
||||
if new_name in discussions:
|
||||
self._set_status(f"discussion '{new_name}' already exists")
|
||||
self.ai_status = f"discussion '{new_name}' already exists"
|
||||
return
|
||||
discussions[new_name] = discussions.pop(old_name)
|
||||
if self.active_discussion == old_name:
|
||||
@@ -2475,7 +2469,7 @@ class AppController:
|
||||
disc_sec = self.project.get("discussion", {})
|
||||
discussions = disc_sec.get("discussions", {})
|
||||
if len(discussions) <= 1:
|
||||
self._set_status("cannot delete the last discussion")
|
||||
self.ai_status = "cannot delete the last discussion"
|
||||
return
|
||||
if name not in discussions:
|
||||
return
|
||||
@@ -2558,7 +2552,7 @@ class AppController:
|
||||
discussions = disc_sec.get("discussions", {})
|
||||
for d_name in discussions:
|
||||
discussions[d_name]["history"] = []
|
||||
self._set_status("session reset")
|
||||
self.ai_status = "session reset"
|
||||
self.ai_response = ""
|
||||
self.ui_ai_input = ""
|
||||
self.ui_manual_approve = False
|
||||
@@ -2581,11 +2575,11 @@ class AppController:
|
||||
md, path, *_ = self._do_generate()
|
||||
self.last_md = md
|
||||
self.last_md_path = path
|
||||
self._set_status(f"md written: {path.name}")
|
||||
self.ai_status = f"md written: {path.name}"
|
||||
# Refresh token budget metrics with CURRENT md
|
||||
self._refresh_api_metrics({}, md_content=md)
|
||||
except Exception as e:
|
||||
self._set_status(f"error: {e}")
|
||||
self.ai_status = f"error: {e}"
|
||||
threading.Thread(target=worker, daemon=True).start()
|
||||
|
||||
def _handle_generate_send(self) -> None:
|
||||
@@ -2600,7 +2594,7 @@ class AppController:
|
||||
self.last_md = md
|
||||
self.last_md_path = path
|
||||
self.last_file_items = file_items
|
||||
self._set_status("sending...")
|
||||
self.ai_status = "sending..."
|
||||
user_msg = self.ui_ai_input
|
||||
|
||||
# RAG Retrieval
|
||||
@@ -2639,7 +2633,7 @@ class AppController:
|
||||
except Exception as e:
|
||||
sys.stderr.write(f"[DEBUG] _do_generate ERROR: {e}\n{traceback.format_exc()}\n")
|
||||
sys.stderr.flush()
|
||||
self._set_status(f"generate error: {e}")
|
||||
self.ai_status = f"generate error: {e}"
|
||||
threading.Thread(target=worker, daemon=True).start()
|
||||
|
||||
def _recalculate_session_usage(self) -> None:
|
||||
@@ -2819,7 +2813,7 @@ class AppController:
|
||||
sys.stderr.write("[DEBUG] _cb_plan_epic _bg_task started\n")
|
||||
sys.stderr.flush()
|
||||
try:
|
||||
self._set_status("Planning Epic (Tier 1)...")
|
||||
self.ai_status = "Planning Epic (Tier 1)..."
|
||||
history = orchestrator_pm.get_track_history_summary()
|
||||
sys.stderr.write(f"[DEBUG] History summary length: {len(history)}\n")
|
||||
sys.stderr.flush()
|
||||
@@ -2856,7 +2850,7 @@ class AppController:
|
||||
"payload": tracks
|
||||
})
|
||||
except Exception as e:
|
||||
self._set_status(f"Epic plan error: {e}")
|
||||
self.ai_status = f"Epic plan error: {e}"
|
||||
print(f"ERROR in _cb_plan_epic background task: {e}")
|
||||
threading.Thread(target=_bg_task, daemon=True).start()
|
||||
|
||||
@@ -2866,7 +2860,7 @@ class AppController:
|
||||
def _bg_task() -> None:
|
||||
sys.stderr.write("[DEBUG] _cb_accept_tracks _bg_task started\n")
|
||||
# Generate skeletons once
|
||||
self._set_status("Phase 2: Generating skeletons for all tracks...")
|
||||
self.ai_status = "Phase 2: Generating skeletons for all tracks..."
|
||||
sys.stderr.write("[DEBUG] Creating ASTParser...\n")
|
||||
parser = ASTParser(language="python")
|
||||
generated_skeletons = ""
|
||||
@@ -2876,7 +2870,7 @@ class AppController:
|
||||
sys.stderr.write(f"[DEBUG] Scanning {len(files_to_scan)} files for skeletons...\n")
|
||||
for i, file_path in enumerate(files_to_scan):
|
||||
try:
|
||||
self._set_status(f"Phase 2: Scanning files ({i+1}/{len(files_to_scan)})...")
|
||||
self.ai_status = f"Phase 2: Scanning files ({i+1}/{len(files_to_scan)})..."
|
||||
abs_path = Path(self.active_project_root) / file_path
|
||||
if abs_path.exists() and abs_path.suffix == ".py":
|
||||
with open(abs_path, "r", encoding="utf-8") as f:
|
||||
@@ -2886,19 +2880,19 @@ class AppController:
|
||||
sys.stderr.write(f"[DEBUG] Error parsing skeleton for {file_path}: {e}\n")
|
||||
except Exception as e:
|
||||
sys.stderr.write(f"[DEBUG] Error in scan loop: {e}\n")
|
||||
self._set_status(f"Error generating skeletons: {e}")
|
||||
self.ai_status = f"Error generating skeletons: {e}"
|
||||
return # Exit if skeleton generation fails
|
||||
sys.stderr.write("[DEBUG] Skeleton generation complete. Starting tracks...\n")
|
||||
# Now loop through tracks and call _start_track_logic with generated skeletons
|
||||
total_tracks = len(self.proposed_tracks)
|
||||
for i, track_data in enumerate(self.proposed_tracks):
|
||||
title = track_data.get("title") or track_data.get("goal", "Untitled Track")
|
||||
self._set_status(f"Processing track {i+1} of {total_tracks}: '{title}'...")
|
||||
self.ai_status = f"Processing track {i+1} of {total_tracks}: '{title}'..."
|
||||
self._start_track_logic(track_data, skeletons_str=generated_skeletons) # Pass skeletons
|
||||
sys.stderr.write("[DEBUG] All tracks started. Refreshing...\n")
|
||||
with self._pending_gui_tasks_lock:
|
||||
self._pending_gui_tasks.append({'action': 'refresh_from_project'}) # Ensure UI refresh after tracks are started
|
||||
self._set_status(f"All {total_tracks} tracks accepted and execution started.")
|
||||
self.ai_status = f"All {total_tracks} tracks accepted and execution started."
|
||||
threading.Thread(target=_bg_task, daemon=True).start()
|
||||
|
||||
def _cb_start_track(self, user_data: Any = None) -> None:
|
||||
@@ -2911,25 +2905,25 @@ class AppController:
|
||||
if self.active_track and self.active_track.id == track_id:
|
||||
# Use the active track object directly to start execution
|
||||
print(f"[DEBUG] _cb_start_track: track_id={self.active_track.id}, desc={self.active_track.description}")
|
||||
self._set_mma_status("running")
|
||||
self.mma_status = "running"
|
||||
engine = multi_agent_conductor.ConductorEngine(self.active_track, self.event_queue, auto_queue=not self.mma_step_mode)
|
||||
self.engines[self.active_track.id] = engine
|
||||
flat = project_manager.flat_config(self.project, self.active_discussion, track_id=self.active_track.id)
|
||||
full_md, _, _ = aggregate.run(flat)
|
||||
threading.Thread(target=engine.run, kwargs={"md_content": full_md}, daemon=True).start()
|
||||
self._set_status(f"Track '{self.active_track.description}' started.")
|
||||
self.ai_status = f"Track '{self.active_track.description}' started."
|
||||
elif self.active_track and self.active_track.id != track_id:
|
||||
# load_track failed but active_track is still wrong - reload explicitly
|
||||
print(f"[DEBUG] _cb_start_track: load failed, trying reload track_id={track_id}")
|
||||
self._cb_load_track(track_id)
|
||||
if self.active_track and self.active_track.id == track_id:
|
||||
self._set_mma_status("running")
|
||||
self.mma_status = "running"
|
||||
engine = multi_agent_conductor.ConductorEngine(self.active_track, self.event_queue, auto_queue=not self.mma_step_mode)
|
||||
self.engines[self.active_track.id] = engine
|
||||
flat = project_manager.flat_config(self.project, self.active_discussion, track_id=self.active_track.id)
|
||||
full_md, _, _ = aggregate.run(flat)
|
||||
threading.Thread(target=engine.run, kwargs={"md_content": full_md}, daemon=True).start()
|
||||
self._set_status(f"Track '{self.active_track.description}' started.")
|
||||
self.ai_status = f"Track '{self.active_track.description}' started."
|
||||
return
|
||||
idx = 0
|
||||
if isinstance(user_data, int):
|
||||
@@ -2940,15 +2934,15 @@ class AppController:
|
||||
track_data = self.proposed_tracks[idx]
|
||||
title = track_data.get("title") or track_data.get("goal", "Untitled Track")
|
||||
threading.Thread(target=lambda: self._start_track_logic(track_data), daemon=True).start()
|
||||
self._set_status(f"Track '{title}' started.")
|
||||
self.ai_status = f"Track '{title}' started."
|
||||
|
||||
def _start_track_logic(self, track_data: dict[str, Any], skeletons_str: str | None = None) -> None:
|
||||
try:
|
||||
goal = track_data.get("goal", "")
|
||||
title = track_data.get("title") or track_data.get("goal", "Untitled Track")
|
||||
self._set_status(f"Phase 2: Generating tickets for {title}...")
|
||||
self.ai_status = f"Phase 2: Generating tickets for {title}..."
|
||||
skeletons = skeletons_str or "" # Use provided skeletons or empty
|
||||
self._set_status("Phase 2: Calling Tech Lead...")
|
||||
self.ai_status = "Phase 2: Calling Tech Lead..."
|
||||
_t2_baseline = len(ai_client.get_comms_log())
|
||||
raw_tickets = conductor_tech_lead.generate_tickets(goal, skeletons)
|
||||
_t2_new = ai_client.get_comms_log()[_t2_baseline:]
|
||||
@@ -2966,10 +2960,10 @@ class AppController:
|
||||
"args": [_t2_in, _t2_out]
|
||||
})
|
||||
if not raw_tickets:
|
||||
self._set_status(f"Error: No tickets generated for track: {title}")
|
||||
self.ai_status = f"Error: No tickets generated for track: {title}"
|
||||
print(f"Warning: No tickets generated for track: {title}")
|
||||
return
|
||||
self._set_status("Phase 2: Sorting tickets...")
|
||||
self.ai_status = "Phase 2: Sorting tickets..."
|
||||
try:
|
||||
sorted_tickets_data = conductor_tech_lead.topological_sort(raw_tickets)
|
||||
except ValueError as e:
|
||||
@@ -3007,7 +3001,7 @@ class AppController:
|
||||
# Start the engine in a separate thread
|
||||
threading.Thread(target=engine.run, kwargs={"md_content": full_md}, daemon=True).start()
|
||||
except Exception as e:
|
||||
self._set_status(f"Track start error: {e}")
|
||||
self.ai_status = f"Track start error: {e}"
|
||||
print(f"ERROR in _start_track_logic: {e}")
|
||||
|
||||
def _cb_ticket_retry(self, ticket_id: str) -> None:
|
||||
@@ -3198,4 +3192,3 @@ class AppController:
|
||||
else:
|
||||
self.active_tickets = []
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user