refactor(sdm): Global pass with refined 'External Only' SDM tags. Pruned redundant internal references and fixed indentation logic in injector. Verified full project compilation.

This commit is contained in:
2026-05-09 14:32:44 -04:00
parent 696c08692e
commit 8c06c1767b
142 changed files with 2352 additions and 990 deletions
+111 -89
View File
@@ -43,7 +43,7 @@ from src import theme_2 as theme
def hide_tk_root() -> Tk:
"""
[C: src/gui_2.py:App._render_files_panel, src/gui_2.py:App._render_projects_panel, src/gui_2.py:App._render_provider_panel, src/gui_2.py:App._render_screenshots_panel, src/gui_2.py:App._render_theme_panel, src/gui_2.py:App.render_path_field]
[C: src/gui_2.py:App._render_files_panel, src/gui_2.py:App._render_projects_panel, src/gui_2.py:App._render_provider_panel, src/gui_2.py:App._render_screenshots_panel, src/gui_2.py:App._render_theme_panel, src/gui_2.py:App.render_path_field]
"""
root = Tk()
root.withdraw()
@@ -53,15 +53,16 @@ def hide_tk_root() -> Tk:
def parse_symbols(text: str) -> list[str]:
"""
Finds all occurrences of '@SymbolName' in text and returns SymbolName.
SymbolName can be a function, class, or method (e.g. @MyClass, @my_func, @MyClass.my_method).
Finds all occurrences of '@SymbolName' in text and returns SymbolName.
SymbolName can be a function, class, or method (e.g. @MyClass, @my_func, @MyClass.my_method).
[C: tests/test_symbol_lookup.py:TestSymbolLookup.test_parse_symbols_basic, tests/test_symbol_lookup.py:TestSymbolLookup.test_parse_symbols_edge_cases, tests/test_symbol_lookup.py:TestSymbolLookup.test_parse_symbols_methods, tests/test_symbol_lookup.py:TestSymbolLookup.test_parse_symbols_mixed, tests/test_symbol_lookup.py:TestSymbolLookup.test_parse_symbols_no_symbols]
"""
return re.findall(r"@([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)", text)
def get_symbol_definition(symbol: str, files: list[str]) -> tuple[str, str, int] | None:
"""
[C: tests/test_symbol_lookup.py:TestSymbolLookup.test_get_symbol_definition_found, tests/test_symbol_lookup.py:TestSymbolLookup.test_get_symbol_definition_not_found]
[C: tests/test_symbol_lookup.py:TestSymbolLookup.test_get_symbol_definition_found, tests/test_symbol_lookup.py:TestSymbolLookup.test_get_symbol_definition_not_found]
"""
for file_path in files:
result = mcp_client.py_get_symbol_info(file_path, symbol)
@@ -83,7 +84,7 @@ class ConfirmRequest(BaseModel):
class ConfirmDialog:
def __init__(self, script: str, base_dir: str) -> None:
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._uid = str(uuid.uuid4())
self._script = str(script) if script is not None else ""
@@ -94,7 +95,7 @@ class ConfirmDialog:
def wait(self) -> tuple[bool, str]:
"""
[C: src/mcp_client.py:StdioMCPServer.stop, src/multi_agent_conductor.py:confirm_execution, src/multi_agent_conductor.py:confirm_spawn, tests/conftest.py:live_gui, tests/test_ai_client_concurrency.py:run_t1, tests/test_ai_client_concurrency.py:run_t2, tests/test_conductor_engine_abort.py:worker, tests/test_parallel_execution.py:test_worker_pool_limit]
[C: src/mcp_client.py:StdioMCPServer.stop, src/multi_agent_conductor.py:confirm_execution, src/multi_agent_conductor.py:confirm_spawn, tests/conftest.py:live_gui, tests/test_ai_client_concurrency.py:run_t1, tests/test_ai_client_concurrency.py:run_t2, tests/test_conductor_engine_abort.py:worker, tests/test_parallel_execution.py:test_worker_pool_limit]
"""
start_time = time.time()
with self._condition:
@@ -107,7 +108,7 @@ class ConfirmDialog:
class MMAApprovalDialog:
def __init__(self, ticket_id: str, payload: str) -> None:
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._payload = payload
self._condition = threading.Condition()
@@ -116,7 +117,7 @@ class MMAApprovalDialog:
def wait(self) -> tuple[bool, str]:
"""
[C: src/mcp_client.py:StdioMCPServer.stop, src/multi_agent_conductor.py:confirm_execution, src/multi_agent_conductor.py:confirm_spawn, tests/conftest.py:live_gui, tests/test_ai_client_concurrency.py:run_t1, tests/test_ai_client_concurrency.py:run_t2, tests/test_conductor_engine_abort.py:worker, tests/test_parallel_execution.py:test_worker_pool_limit]
[C: src/mcp_client.py:StdioMCPServer.stop, src/multi_agent_conductor.py:confirm_execution, src/multi_agent_conductor.py:confirm_spawn, tests/conftest.py:live_gui, tests/test_ai_client_concurrency.py:run_t1, tests/test_ai_client_concurrency.py:run_t2, tests/test_conductor_engine_abort.py:worker, tests/test_parallel_execution.py:test_worker_pool_limit]
"""
start_time = time.time()
with self._condition:
@@ -129,7 +130,7 @@ class MMAApprovalDialog:
class MMASpawnApprovalDialog:
def __init__(self, ticket_id: str, role: str, prompt: str, context_md: str) -> None:
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._prompt = prompt
self._context_md = context_md
@@ -140,7 +141,7 @@ class MMASpawnApprovalDialog:
def wait(self) -> dict[str, Any]:
"""
[C: src/mcp_client.py:StdioMCPServer.stop, src/multi_agent_conductor.py:confirm_execution, src/multi_agent_conductor.py:confirm_spawn, tests/conftest.py:live_gui, tests/test_ai_client_concurrency.py:run_t1, tests/test_ai_client_concurrency.py:run_t2, tests/test_conductor_engine_abort.py:worker, tests/test_parallel_execution.py:test_worker_pool_limit]
[C: src/mcp_client.py:StdioMCPServer.stop, src/multi_agent_conductor.py:confirm_execution, src/multi_agent_conductor.py:confirm_spawn, tests/conftest.py:live_gui, tests/test_ai_client_concurrency.py:run_t1, tests/test_ai_client_concurrency.py:run_t2, tests/test_conductor_engine_abort.py:worker, tests/test_parallel_execution.py:test_worker_pool_limit]
"""
start_time = time.time()
with self._condition:
@@ -157,15 +158,14 @@ class MMASpawnApprovalDialog:
class AppController:
"""
The headless controller for the Manual Slop application.
Owns the application state and manages background services.
The headless controller for the Manual Slop application.
Owns the application state and manages background services.
"""
def __init__(self):
# Initialize locks first to avoid initialization order issues
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._send_thread_lock: threading.Lock = threading.Lock()
self._disc_entries_lock: threading.Lock = threading.Lock()
@@ -517,7 +517,8 @@ class AppController:
def _update_inject_preview(self) -> None:
"""
Updates the preview content based on the selected file and injection mode.
Updates the preview content based on the selected file and injection mode.
[C: src/gui_2.py:App._gui_func, tests/test_skeleton_injection.py:test_update_inject_preview_full, tests/test_skeleton_injection.py:test_update_inject_preview_skeleton, tests/test_skeleton_injection.py:test_update_inject_preview_truncation]
"""
if not self._inject_file_path:
@@ -754,7 +755,7 @@ class AppController:
def _process_pending_gui_tasks(self) -> None:
# Periodic telemetry broadcast
"""
[C: src/gui_2.py:App._gui_func, tests/test_api_hook_extensions.py:test_app_processes_new_actions, tests/test_gui_updates.py:test_gui_updates_on_event, tests/test_live_gui_integration_v2.py:test_user_request_error_handling, tests/test_live_gui_integration_v2.py:test_user_request_integration_flow, tests/test_mma_orchestration_gui.py:test_handle_ai_response_fallback, tests/test_mma_orchestration_gui.py:test_handle_ai_response_with_stream_id, tests/test_mma_orchestration_gui.py:test_process_pending_gui_tasks_mma_spawn_approval, tests/test_mma_orchestration_gui.py:test_process_pending_gui_tasks_show_track_proposal, tests/test_process_pending_gui_tasks.py:test_gcli_path_updates_adapter, tests/test_process_pending_gui_tasks.py:test_redundant_calls_in_process_pending_gui_tasks]
[C: src/gui_2.py:App._gui_func, tests/test_api_hook_extensions.py:test_app_processes_new_actions, tests/test_gui_updates.py:test_gui_updates_on_event, tests/test_live_gui_integration_v2.py:test_user_request_error_handling, tests/test_live_gui_integration_v2.py:test_user_request_integration_flow, tests/test_mma_orchestration_gui.py:test_handle_ai_response_fallback, tests/test_mma_orchestration_gui.py:test_handle_ai_response_with_stream_id, tests/test_mma_orchestration_gui.py:test_process_pending_gui_tasks_mma_spawn_approval, tests/test_mma_orchestration_gui.py:test_process_pending_gui_tasks_show_track_proposal, tests/test_process_pending_gui_tasks.py:test_gcli_path_updates_adapter, tests/test_process_pending_gui_tasks.py:test_redundant_calls_in_process_pending_gui_tasks]
"""
now = time.time()
if hasattr(self, 'event_queue') and hasattr(self.event_queue, 'websocket_server') and self.event_queue.websocket_server:
@@ -955,7 +956,7 @@ class AppController:
def __init__(self, t): self.t = t
def wait(self):
"""
[C: src/mcp_client.py:StdioMCPServer.stop, src/multi_agent_conductor.py:confirm_execution, src/multi_agent_conductor.py:confirm_spawn, tests/conftest.py:live_gui, tests/test_ai_client_concurrency.py:run_t1, tests/test_ai_client_concurrency.py:run_t2, tests/test_conductor_engine_abort.py:worker, tests/test_parallel_execution.py:test_worker_pool_limit]
[C: src/mcp_client.py:StdioMCPServer.stop, src/multi_agent_conductor.py:confirm_execution, src/multi_agent_conductor.py:confirm_spawn, tests/conftest.py:live_gui, tests/test_ai_client_concurrency.py:run_t1, tests/test_ai_client_concurrency.py:run_t2, tests/test_conductor_engine_abort.py:worker, tests/test_parallel_execution.py:test_worker_pool_limit]
"""
return {'approved': True, 'abort': False, 'prompt': self.t.get("prompt"), 'context_md': self.t.get("context_md")}
task["dialog_container"][0] = AutoSpawnDialog(task)
@@ -1027,7 +1028,8 @@ class AppController:
def _process_pending_history_adds(self) -> None:
"""
Synchronizes pending history entries to the active discussion and project state.
Synchronizes pending history entries to the active discussion and project state.
[C: src/gui_2.py:App._gui_func]
"""
with self._pending_history_adds_lock:
@@ -1055,7 +1057,8 @@ class AppController:
def _process_pending_tool_calls(self) -> bool:
"""
Drains pending tool calls into the tool log. Returns True if any were processed.
Drains pending tool calls into the tool log. Returns True if any were processed.
[C: src/gui_2.py:App._gui_func]
"""
with self._pending_tool_calls_lock:
@@ -1102,7 +1105,8 @@ class AppController:
def init_state(self):
"""
Initializes the application state from configurations.
Initializes the application state from configurations.
[C: src/gui_2.py:App.__init__, src/gui_2.py:App._render_paths_panel, src/gui_2.py:App._save_paths, tests/test_app_controller_mcp.py:test_app_controller_mcp_loading, tests/test_app_controller_mcp.py:test_app_controller_mcp_project_override, tests/test_external_mcp_e2e.py:test_external_mcp_e2e_refresh_and_call, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_app_controller_init_state_loads_prompts]
"""
self.active_tickets = []
@@ -1247,7 +1251,7 @@ class AppController:
async def refresh_external_mcps(self):
"""
[C: tests/test_external_mcp_e2e.py:test_external_mcp_e2e_refresh_and_call]
[C: tests/test_external_mcp_e2e.py:test_external_mcp_e2e_refresh_and_call]
"""
await mcp_client.get_external_mcp_manager().stop_all()
# Start servers with auto_start=True
@@ -1257,7 +1261,7 @@ class AppController:
def cb_load_prior_log(self, path: Optional[str] = None) -> None:
"""
[C: src/gui_2.py:App._render_log_management]
[C: src/gui_2.py:App._render_log_management]
"""
root = hide_tk_root()
if path is None:
@@ -1448,7 +1452,7 @@ class AppController:
def cb_exit_prior_session(self):
"""
[C: src/gui_2.py:App._render_comms_history_panel, src/gui_2.py:App._render_discussion_panel]
[C: src/gui_2.py:App._render_comms_history_panel, src/gui_2.py:App._render_discussion_panel]
"""
self.is_viewing_prior_session = False
if self._current_session_usage:
@@ -1533,7 +1537,7 @@ class AppController:
def _fetch_models(self, provider: str) -> None:
"""
[C: src/gui_2.py:App.run]
[C: src/gui_2.py:App.run]
"""
self.ai_status = "fetching models..."
@@ -1559,7 +1563,8 @@ class AppController:
def start_services(self, app: Any = None):
"""
Starts background threads.
Starts background threads.
[C: src/gui_2.py:App.__init__]
"""
self._prune_old_logs()
@@ -1569,7 +1574,8 @@ class AppController:
def shutdown(self) -> None:
"""
Stops background threads and cleans up resources.
Stops background threads and cleans up resources.
[C: src/gui_2.py:App.run, src/gui_2.py:App.shutdown, tests/conftest.py:app_instance, tests/conftest.py:mock_app]
"""
from src import ai_client
@@ -1671,7 +1677,8 @@ class AppController:
def _handle_request_event(self, event: events.UserRequestEvent) -> None:
"""
Processes a UserRequestEvent by calling the AI client.
Processes a UserRequestEvent by calling the AI client.
[C: tests/test_live_gui_integration_v2.py:test_user_request_error_handling, tests/test_live_gui_integration_v2.py:test_user_request_integration_flow, tests/test_rag_integration.py:test_rag_integration]
"""
self.ai_status = 'sending...'
@@ -1732,7 +1739,7 @@ class AppController:
def _on_comms_entry(self, entry: Dict[str, Any]) -> None:
"""
[C: tests/test_app_controller_offloading.py:test_on_comms_entry_tool_result_offloading]
[C: tests/test_app_controller_offloading.py:test_on_comms_entry_tool_result_offloading]
"""
optimized_entry = self._offload_entry_payload(entry)
session_logger.log_comms(optimized_entry)
@@ -1829,7 +1836,7 @@ class AppController:
def _on_tool_log(self, script: str, result: str) -> None:
"""
[C: tests/test_app_controller_offloading.py:test_on_tool_log_offloading]
[C: tests/test_app_controller_offloading.py:test_on_tool_log_offloading]
"""
session_logger.log_tool_call(script, result, None)
session_logger.log_tool_output(result)
@@ -1839,7 +1846,7 @@ class AppController:
def _on_api_event(self, event_name: str = "generic_event", **kwargs: Any) -> None:
"""
[C: tests/test_gui_updates.py:test_gui_updates_on_event]
[C: tests/test_gui_updates.py:test_gui_updates_on_event]
"""
payload = kwargs.get("payload", {})
# Push to background event queue, NOT GUI queue
@@ -1857,7 +1864,7 @@ 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]:
"""
[C: tests/test_arch_boundary_phase2.py:TestArchBoundaryPhase2.test_mutating_tool_triggers_callback, tests/test_arch_boundary_phase2.py:TestArchBoundaryPhase2.test_rejection_prevents_dispatch]
[C: tests/test_arch_boundary_phase2.py:TestArchBoundaryPhase2.test_mutating_tool_triggers_callback, tests/test_arch_boundary_phase2.py:TestArchBoundaryPhase2.test_rejection_prevents_dispatch]
"""
if self.test_hooks_enabled and not getattr(self, "ui_manual_approve", False):
self.ai_status = "running powershell..."
@@ -1897,7 +1904,7 @@ class AppController:
def _append_tool_log(self, script: str, result: str, source_tier: str | None = None, elapsed_ms: float = 0.0) -> None:
"""
[C: tests/test_mma_agent_focus_phase1.py:test_append_tool_log_dict_has_source_tier, tests/test_mma_agent_focus_phase1.py:test_append_tool_log_dict_keys, tests/test_mma_agent_focus_phase1.py:test_append_tool_log_stores_dict]
[C: tests/test_mma_agent_focus_phase1.py:test_append_tool_log_dict_has_source_tier, tests/test_mma_agent_focus_phase1.py:test_append_tool_log_dict_keys, tests/test_mma_agent_focus_phase1.py:test_append_tool_log_stores_dict]
"""
self._tool_log.append({"script": script, "result": result, "ts": time.time(), "source_tier": source_tier})
tool_name = self._extract_tool_name(script)
@@ -1995,7 +2002,8 @@ class AppController:
def create_api(self) -> FastAPI:
"""
Creates and configures the FastAPI application for headless mode.
Creates and configures the FastAPI application for headless mode.
[C: src/gui_2.py:App.run, tests/test_headless_service.py:TestHeadlessAPI.setUp]
"""
api = FastAPI(title="Manual Slop Headless API")
@@ -2022,7 +2030,8 @@ class AppController:
@api.get("/api/gui/state", dependencies=[Depends(get_api_key)])
def get_gui_state() -> dict[str, Any]:
"""
Returns the current GUI state for specific fields.
Returns the current GUI state for specific fields.
[C: tests/test_ai_settings_layout.py:test_change_provider_via_hook, tests/test_ai_settings_layout.py:test_set_params_via_custom_callback, tests/test_conductor_api_hook_integration.py:simulate_conductor_phase_completion, tests/test_external_editor_gui.py:test_button_click_is_received, tests/test_external_editor_gui.py:test_patch_modal_shows_with_configured_editor, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_text_viewer.py:test_text_viewer_state_update, tests/test_hooks.py:test_live_hook_server_responses, tests/test_live_gui_integration_v2.py:test_api_gui_state_live, tests/test_live_workflow.py:test_full_live_workflow, tests/test_live_workflow.py:wait_for_value, tests/test_patch_modal_gui.py:test_patch_apply_modal_workflow, tests/test_patch_modal_gui.py:test_patch_modal_appears_on_trigger, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim, tests/test_saved_presets_sim.py:test_preset_manager_modal, tests/test_saved_presets_sim.py:test_preset_switching, tests/test_task_dag_popout_sim.py:test_task_dag_popout, tests/test_tool_management_layout.py:test_tool_management_gettable_fields, tests/test_tool_management_layout.py:test_tool_management_state_updates, tests/test_tool_presets_sim.py:test_tool_preset_switching, tests/test_usage_analytics_popout_sim.py:test_usage_analytics_popout]
"""
gettable = getattr(self, "_gettable_fields", {})
@@ -2039,7 +2048,8 @@ class AppController:
@api.get("/api/gui/mma_status", dependencies=[Depends(get_api_key)])
def get_mma_status() -> dict[str, Any]:
"""
Dedicated endpoint for MMA-related status.
Dedicated endpoint for MMA-related status.
[C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_live_workflow.py:test_full_live_workflow, tests/test_mma_concurrent_tracks_sim.py:_poll_mma_status, tests/test_mma_concurrent_tracks_sim.py:test_mma_concurrent_tracks_execution, tests/test_mma_concurrent_tracks_stress_sim.py:test_mma_concurrent_tracks_stress, tests/test_mma_step_mode_sim.py:_poll_mma_status, tests/test_mma_step_mode_sim.py:test_mma_step_mode_approval_flow, tests/test_visual_orchestration.py:test_mma_epic_lifecycle, tests/test_visual_sim_gui_ux.py:test_gui_ux_event_routing, tests/test_visual_sim_mma_v2.py:_poll]
"""
return {
@@ -2057,7 +2067,8 @@ class AppController:
@api.post("/api/gui", dependencies=[Depends(get_api_key)])
def post_gui(req: dict) -> dict[str, str]:
"""
Pushes a GUI task to the event queue.
Pushes a GUI task to the event queue.
[C: tests/test_ai_settings_layout.py:test_set_params_via_custom_callback, tests/test_api_hook_client.py:test_post_gui_success, tests/test_gui2_parity.py:test_gui2_custom_callback_hook_works, tests/test_gui2_parity.py:test_gui2_set_value_hook_works, tests/test_visual_mma.py:test_visual_mma_components]
"""
self.event_queue.put("gui_task", req)
@@ -2085,7 +2096,8 @@ class AppController:
@api.get("/api/performance", dependencies=[Depends(get_api_key)])
def get_performance() -> dict[str, Any]:
"""
Returns performance monitor metrics.
Returns performance monitor metrics.
[C: tests/test_gui2_performance.py:test_performance_benchmarking, tests/test_gui_performance_requirements.py:test_idle_performance_requirements, tests/test_gui_stress_performance.py:test_comms_volume_stress_performance, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_visual_sim_gui_ux.py:test_gui_ux_event_routing]
"""
return {"performance": self.perf_monitor.get_metrics()}
@@ -2207,7 +2219,8 @@ class AppController:
@api.get("/api/v1/sessions/{session_id}", dependencies=[Depends(get_api_key)])
def get_session(session_id: str) -> dict[str, Any]:
"""
Returns the content of the comms.log for a specific session.
Returns the content of the comms.log for a specific session.
[C: simulation/ping_pong.py:main, simulation/sim_context.py:ContextSimulation.run, simulation/sim_execution.py:ExecutionSimulation.run, simulation/sim_tools.py:ToolsSimulation.run, simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async, simulation/workflow_sim.py:WorkflowSimulator.wait_for_ai_response, tests/test_api_hook_client.py:test_get_session_success, tests/test_gui_stress_performance.py:test_comms_volume_stress_performance, tests/test_live_workflow.py:test_full_live_workflow, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim]
"""
log_path = paths.get_logs_dir() / session_id / "comms.log"
@@ -2265,14 +2278,14 @@ class AppController:
def _cb_reset_base_prompt(self, user_data=None) -> None:
"""
[C: src/gui_2.py:App._render_system_prompts_panel]
[C: src/gui_2.py:App._render_system_prompts_panel]
"""
self.ui_base_system_prompt = ai_client._SYSTEM_PROMPT
self.ui_use_default_base_prompt = False
def _cb_clear_summary_cache(self, user_data=None) -> None:
"""
[C: src/gui_2.py:App._render_files_panel]
[C: src/gui_2.py:App._render_files_panel]
"""
from src import summarize
summarize._summary_cache.clear()
@@ -2280,7 +2293,7 @@ class AppController:
def _cb_show_base_prompt_diff(self, user_data=None) -> None:
"""
[C: src/gui_2.py:App._render_system_prompts_panel]
[C: src/gui_2.py:App._render_system_prompts_panel]
"""
self._show_base_prompt_diff_modal = True
@@ -2292,7 +2305,7 @@ class AppController:
def _switch_project(self, path: str) -> None:
"""
[C: src/gui_2.py:App._render_projects_panel]
[C: src/gui_2.py:App._render_projects_panel]
"""
if not Path(path).exists():
self.ai_status = f"project file not found: {path}"
@@ -2315,7 +2328,7 @@ class AppController:
def _refresh_from_project(self) -> None:
# Deserialize FileItems in files.paths
"""
[C: tests/test_mma_dashboard_refresh.py:test_mma_dashboard_initialization_refresh, tests/test_mma_dashboard_refresh.py:test_mma_dashboard_refresh]
[C: tests/test_mma_dashboard_refresh.py:test_mma_dashboard_initialization_refresh, tests/test_mma_dashboard_refresh.py:test_mma_dashboard_refresh]
"""
raw_paths = self.project.get("files", {}).get("paths", [])
self.files = []
@@ -2397,7 +2410,7 @@ class AppController:
def _cb_save_workspace_profile(self, name: str, scope: str = 'project') -> None:
"""
[C: src/gui_2.py:App._render_save_workspace_profile_modal]
[C: src/gui_2.py:App._render_save_workspace_profile_modal]
"""
if not hasattr(self, '_app') or not self._app:
return
@@ -2408,7 +2421,7 @@ class AppController:
def _cb_delete_workspace_profile(self, name: str, scope: str = 'project') -> None:
"""
[C: src/gui_2.py:App._show_menus]
[C: src/gui_2.py:App._show_menus]
"""
self.workspace_manager.delete_profile(name, scope=scope)
self.workspace_profiles = self.workspace_manager.load_all_profiles()
@@ -2417,7 +2430,7 @@ class AppController:
def _cb_load_workspace_profile(self, name: str) -> None:
"""
[C: src/gui_2.py:App._show_menus]
[C: src/gui_2.py:App._show_menus]
"""
if name in self.workspace_profiles:
profile = self.workspace_profiles[name]
@@ -2426,7 +2439,7 @@ class AppController:
def _apply_preset(self, name: str, scope: str) -> None:
"""
[C: src/gui_2.py:App._render_system_prompts_panel]
[C: src/gui_2.py:App._render_system_prompts_panel]
"""
print(f"[DEBUG] _apply_preset: name={name}, scope={scope}")
if name == "None":
@@ -2448,7 +2461,7 @@ class AppController:
def _cb_save_preset(self, name, content, scope):
"""
[C: src/gui_2.py:App._render_preset_manager_content]
[C: src/gui_2.py:App._render_preset_manager_content]
"""
print(f"[DEBUG] _cb_save_preset: name={name}, scope={scope}")
if not name or not name.strip():
@@ -2463,14 +2476,14 @@ class AppController:
def _cb_delete_preset(self, name, scope):
"""
[C: src/gui_2.py:App._render_preset_manager_content]
[C: src/gui_2.py:App._render_preset_manager_content]
"""
self.preset_manager.delete_preset(name, scope)
self.presets = self.preset_manager.load_all()
def _cb_save_tool_preset(self, name, categories, scope):
"""
[C: src/gui_2.py:App._render_tool_preset_manager_content]
[C: src/gui_2.py:App._render_tool_preset_manager_content]
"""
preset = models.ToolPreset(name=name, categories=categories)
self.tool_preset_manager.save_preset(preset, scope)
@@ -2478,14 +2491,14 @@ class AppController:
def _cb_delete_tool_preset(self, name, scope):
"""
[C: src/gui_2.py:App._render_tool_preset_manager_content]
[C: src/gui_2.py:App._render_tool_preset_manager_content]
"""
self.tool_preset_manager.delete_preset(name, scope)
self.tool_presets = self.tool_preset_manager.load_all_presets()
def _cb_save_bias_profile(self, profile: models.BiasProfile, scope: str = "project"):
"""
[C: src/gui_2.py:App._render_tool_preset_manager_content]
[C: src/gui_2.py:App._render_tool_preset_manager_content]
"""
self.tool_preset_manager.save_bias_profile(profile, scope)
self.bias_profiles = self.tool_preset_manager.load_all_bias_profiles()
@@ -2496,14 +2509,14 @@ class AppController:
def _cb_save_persona(self, persona: models.Persona, scope: str = "project") -> None:
"""
[C: src/gui_2.py:App._render_persona_editor_window]
[C: src/gui_2.py:App._render_persona_editor_window]
"""
self.persona_manager.save_persona(persona, scope)
self.personas = self.persona_manager.load_all()
def _cb_delete_persona(self, name: str, scope: str = "project") -> None:
"""
[C: src/gui_2.py:App._render_persona_editor_window]
[C: src/gui_2.py:App._render_persona_editor_window]
"""
self.persona_manager.delete_persona(name, scope)
self.personas = self.persona_manager.load_all()
@@ -2511,7 +2524,7 @@ class AppController:
def _cb_load_track(self, track_id: str) -> None:
"""
[C: src/gui_2.py:App._render_mma_dashboard]
[C: src/gui_2.py:App._render_mma_dashboard]
"""
state = project_manager.load_track_state(track_id, self.active_project_root)
if state:
@@ -2545,7 +2558,7 @@ class AppController:
def _save_active_project(self) -> None:
"""
[C: src/gui_2.py:App.delete_context_preset, src/gui_2.py:App.save_context_preset]
[C: src/gui_2.py:App.delete_context_preset, src/gui_2.py:App.save_context_preset]
"""
if self.active_project_path:
try:
@@ -2556,7 +2569,7 @@ class AppController:
def _get_discussion_names(self) -> list[str]:
"""
[C: src/gui_2.py:App._render_discussion_panel]
[C: src/gui_2.py:App._render_discussion_panel]
"""
disc_sec = self.project.get("discussion", {})
discussions = disc_sec.get("discussions", {})
@@ -2564,7 +2577,7 @@ class AppController:
def _switch_discussion(self, name: str) -> None:
"""
[C: src/gui_2.py:App._render_discussion_panel, src/gui_2.py:App._render_takes_panel]
[C: src/gui_2.py:App._render_discussion_panel, src/gui_2.py:App._render_takes_panel]
"""
self._flush_disc_entries_to_project()
disc_sec = self.project.get("discussion", {})
@@ -2582,7 +2595,7 @@ class AppController:
def _flush_disc_entries_to_project(self) -> None:
"""
[C: src/gui_2.py:App._render_discussion_panel]
[C: src/gui_2.py:App._render_discussion_panel]
"""
history_strings = [project_manager.entry_to_str(e) for e in self.disc_entries]
if self.active_track and self._track_discussion_active:
@@ -2596,7 +2609,7 @@ class AppController:
def _create_discussion(self, name: str) -> None:
"""
[C: src/gui_2.py:App._render_discussion_panel, src/gui_2.py:App._render_synthesis_panel, src/gui_2.py:App._render_takes_panel]
[C: src/gui_2.py:App._render_discussion_panel, src/gui_2.py:App._render_synthesis_panel, src/gui_2.py:App._render_takes_panel]
"""
disc_sec = self.project.setdefault("discussion", {})
discussions = disc_sec.setdefault("discussions", {})
@@ -2608,7 +2621,7 @@ class AppController:
def _branch_discussion(self, index: int) -> None:
"""
[C: src/gui_2.py:App._render_discussion_panel]
[C: src/gui_2.py:App._render_discussion_panel]
"""
self._flush_disc_entries_to_project()
# Generate a unique branch name
@@ -2625,7 +2638,7 @@ class AppController:
self._switch_discussion(new_name)
def _rename_discussion(self, old_name: str, new_name: str) -> None:
"""
[C: src/gui_2.py:App._render_discussion_panel]
[C: src/gui_2.py:App._render_discussion_panel]
"""
disc_sec = self.project.get("discussion", {})
discussions = disc_sec.get("discussions", {})
@@ -2641,7 +2654,7 @@ class AppController:
def _delete_discussion(self, name: str) -> None:
"""
[C: src/gui_2.py:App._render_discussion_panel]
[C: src/gui_2.py:App._render_discussion_panel]
"""
disc_sec = self.project.get("discussion", {})
discussions = disc_sec.get("discussions", {})
@@ -2657,7 +2670,7 @@ class AppController:
def _handle_mma_respond(self, approved: bool, payload: str | None = None, abort: bool = False, prompt: str | None = None, context_md: str | None = None) -> None:
"""
[C: src/gui_2.py:App._gui_func, src/gui_2.py:App._handle_approve_mma_step, src/gui_2.py:App._handle_approve_spawn]
[C: src/gui_2.py:App._gui_func, src/gui_2.py:App._handle_approve_mma_step, src/gui_2.py:App._handle_approve_spawn]
"""
if self._pending_mma_approvals:
task = self._pending_mma_approvals.pop(0)
@@ -2685,7 +2698,8 @@ class AppController:
def _handle_approve_ask(self) -> None:
"""
Responds with approval for a pending /api/ask request.
Responds with approval for a pending /api/ask request.
[C: src/gui_2.py:App.__init__, src/gui_2.py:App._gui_func]
"""
if not self._ask_request_id: return
@@ -2706,7 +2720,8 @@ class AppController:
def _handle_reject_ask(self) -> None:
"""
Responds with rejection for a pending /api/ask request.
Responds with rejection for a pending /api/ask request.
[C: src/gui_2.py:App._gui_func]
"""
if not self._ask_request_id: return
@@ -2727,7 +2742,8 @@ class AppController:
def _handle_reset_session(self) -> None:
"""
Logic for resetting the AI session and GUI state.
Logic for resetting the AI session and GUI state.
[C: src/gui_2.py:App._render_message_panel]
"""
ai_client.reset_session()
@@ -2758,13 +2774,14 @@ class AppController:
def _handle_md_only(self) -> None:
"""
Logic for the 'MD Only' action.
Logic for the 'MD Only' action.
[C: src/gui_2.py:App._render_message_panel]
"""
def worker():
"""
[C: tests/test_symbol_parsing.py:test_handle_generate_send_appends_definitions, tests/test_symbol_parsing.py:test_handle_generate_send_no_symbols]
[C: tests/test_symbol_parsing.py:test_handle_generate_send_appends_definitions, tests/test_symbol_parsing.py:test_handle_generate_send_no_symbols]
"""
try:
md, path, *_ = self._do_generate()
@@ -2779,13 +2796,14 @@ class AppController:
def _handle_generate_send(self) -> None:
"""
Logic for the 'Gen + Send' action.
Logic for the 'Gen + Send' action.
[C: src/gui_2.py:App._render_message_panel, src/gui_2.py:App._render_synthesis_panel, src/gui_2.py:App._render_takes_panel, tests/test_gui_events_v2.py:test_handle_generate_send_pushes_event, tests/test_symbol_parsing.py:test_handle_generate_send_appends_definitions, tests/test_symbol_parsing.py:test_handle_generate_send_no_symbols]
"""
def worker():
"""
[C: tests/test_symbol_parsing.py:test_handle_generate_send_appends_definitions, tests/test_symbol_parsing.py:test_handle_generate_send_no_symbols]
[C: tests/test_symbol_parsing.py:test_handle_generate_send_appends_definitions, tests/test_symbol_parsing.py:test_handle_generate_send_no_symbols]
"""
sys.stderr.write("[DEBUG] _handle_generate_send worker started\n")
sys.stderr.flush()
@@ -2852,7 +2870,7 @@ class AppController:
def _refresh_api_metrics(self, payload: dict[str, Any], md_content: str | None = None) -> None:
"""
[C: tests/test_gui_updates.py:test_telemetry_data_updates_correctly]
[C: tests/test_gui_updates.py:test_telemetry_data_updates_correctly]
"""
if "latency" in payload:
self.session_usage["last_latency"] = payload["latency"]
@@ -2879,7 +2897,7 @@ class AppController:
def clear_cache(self) -> None:
"""
[C: src/gui_2.py:App._render_cache_panel]
[C: src/gui_2.py:App._render_cache_panel]
"""
from src import ai_client
ai_client.cleanup()
@@ -2887,7 +2905,7 @@ class AppController:
def get_session_insights(self) -> Dict[str, Any]:
"""
[C: src/gui_2.py:App._render_session_insights_panel]
[C: src/gui_2.py:App._render_session_insights_panel]
"""
from src import cost_tracker
total_input = sum(e["input"] for e in self._token_history)
@@ -2912,7 +2930,7 @@ class AppController:
def _flush_to_project(self) -> None:
"""
[C: src/gui_2.py:App._gui_func, src/gui_2.py:App._render_discussion_panel, src/gui_2.py:App._render_projects_panel, src/gui_2.py:App._show_menus]
[C: src/gui_2.py:App._gui_func, src/gui_2.py:App._render_discussion_panel, src/gui_2.py:App._render_projects_panel, src/gui_2.py:App._show_menus]
"""
proj = self.project
proj.setdefault("output", {})["output_dir"] = self.ui_output_dir
@@ -2952,7 +2970,7 @@ class AppController:
def _flush_to_config(self) -> None:
"""
[C: src/gui_2.py:App._gui_func, src/gui_2.py:App._render_discussion_panel, src/gui_2.py:App._render_projects_panel, src/gui_2.py:App._render_theme_panel, src/gui_2.py:App._show_menus, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_app_controller_flush_saves_prompts]
[C: src/gui_2.py:App._gui_func, src/gui_2.py:App._render_discussion_panel, src/gui_2.py:App._render_projects_panel, src/gui_2.py:App._render_theme_panel, src/gui_2.py:App._show_menus, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_app_controller_flush_saves_prompts]
"""
self.config["ai"] = {
"provider": self.current_provider,
@@ -2995,7 +3013,8 @@ class AppController:
def _do_generate(self) -> tuple[str, Path, list[dict[str, Any]], str, str]:
"""
Returns (full_md, output_path, file_items, stable_md, discussion_text).
Returns (full_md, output_path, file_items, stable_md, discussion_text).
[C: src/gui_2.py:App._show_menus, tests/test_tiered_aggregation.py:test_app_controller_do_generate_uses_persona_strategy]
"""
self._flush_to_project()
@@ -3029,7 +3048,7 @@ class AppController:
def _cb_plan_epic(self) -> None:
"""
[C: src/gui_2.py:App._render_mma_dashboard, tests/test_mma_orchestration_gui.py:test_cb_plan_epic_launches_thread]
[C: src/gui_2.py:App._render_mma_dashboard, tests/test_mma_orchestration_gui.py:test_cb_plan_epic_launches_thread]
"""
def _bg_task() -> None:
sys.stderr.write("[DEBUG] _cb_plan_epic _bg_task started\n")
@@ -3078,7 +3097,7 @@ class AppController:
def _cb_accept_tracks(self) -> None:
"""
[C: src/gui_2.py:App._render_track_proposal_modal]
[C: src/gui_2.py:App._render_track_proposal_modal]
"""
self._show_track_proposal_modal = False
@@ -3122,7 +3141,7 @@ class AppController:
def _cb_start_track(self, user_data: Any = None) -> None:
"""
[C: src/gui_2.py:App._render_track_proposal_modal]
[C: src/gui_2.py:App._render_track_proposal_modal]
"""
if isinstance(user_data, str):
# If track_id is provided directly
@@ -3234,7 +3253,7 @@ class AppController:
def _cb_ticket_retry(self, ticket_id: str) -> None:
"""
[C: tests/test_mma_ticket_actions.py:test_cb_ticket_retry]
[C: tests/test_mma_ticket_actions.py:test_cb_ticket_retry]
"""
for t in self.active_tickets:
if t.get('id') == ticket_id:
@@ -3244,7 +3263,7 @@ class AppController:
def _cb_ticket_skip(self, ticket_id: str) -> None:
"""
[C: tests/test_mma_ticket_actions.py:test_cb_ticket_skip]
[C: tests/test_mma_ticket_actions.py:test_cb_ticket_skip]
"""
for t in self.active_tickets:
if t.get('id') == ticket_id:
@@ -3266,7 +3285,8 @@ class AppController:
def kill_worker(self, worker_id: str) -> None:
"""
Aborts a running worker.
Aborts a running worker.
[C: src/gui_2.py:App._cb_kill_ticket, tests/test_conductor_engine_abort.py:test_kill_worker_sets_abort_and_joins_thread]
"""
engine = self.engines.get(self.active_track.id if self.active_track else None)
@@ -3289,7 +3309,8 @@ class AppController:
def inject_context(self, data: dict) -> None:
"""
Programmatic context injection.
Programmatic context injection.
[C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation]
"""
file_path = data.get("file_path")
@@ -3338,7 +3359,7 @@ class AppController:
def _cb_run_conductor_setup(self) -> None:
"""
[C: src/gui_2.py:App._render_mma_dashboard, tests/test_gui_phase3.py:test_conductor_setup_scan]
[C: src/gui_2.py:App._render_mma_dashboard, tests/test_gui_phase3.py:test_conductor_setup_scan]
"""
base = paths.get_conductor_dir(project_path=self.active_project_root)
if not base.exists():
@@ -3368,7 +3389,7 @@ class AppController:
def _cb_create_track(self, name: str, desc: str, track_type: str) -> None:
"""
[C: src/gui_2.py:App._render_mma_dashboard, tests/test_gui_phase3.py:test_create_track]
[C: src/gui_2.py:App._render_mma_dashboard, tests/test_gui_phase3.py:test_create_track]
"""
if not name: return
date_suffix = datetime.now().strftime("%Y%m%d")
@@ -3396,7 +3417,7 @@ class AppController:
def _push_mma_state_update(self) -> None:
"""
[C: src/gui_2.py:App._cb_block_ticket, src/gui_2.py:App._cb_unblock_ticket, src/gui_2.py:App._render_mma_dashboard, src/gui_2.py:App._render_task_dag_panel, src/gui_2.py:App._render_ticket_queue, src/gui_2.py:App._reorder_ticket, src/gui_2.py:App.bulk_block, src/gui_2.py:App.bulk_execute, src/gui_2.py:App.bulk_skip, tests/test_gui_phase4.py:test_push_mma_state_update]
[C: src/gui_2.py:App._cb_block_ticket, src/gui_2.py:App._cb_unblock_ticket, src/gui_2.py:App._render_mma_dashboard, src/gui_2.py:App._render_task_dag_panel, src/gui_2.py:App._render_ticket_queue, src/gui_2.py:App._reorder_ticket, src/gui_2.py:App.bulk_block, src/gui_2.py:App.bulk_execute, src/gui_2.py:App.bulk_skip, tests/test_gui_phase4.py:test_push_mma_state_update]
"""
if not self.active_track:
return
@@ -3420,7 +3441,8 @@ class AppController:
def _load_active_tickets(self) -> None:
"""
Populates self.active_tickets based on the current execution mode.
Populates self.active_tickets based on the current execution mode.
[C: tests/test_gui_dag_beads.py:test_load_active_tickets_from_beads]
"""
if getattr(self, "ui_project_execution_mode", "native") == "beads":
@@ -3442,4 +3464,4 @@ class AppController:
if self.active_track:
self.active_tickets = [asdict(t) if not isinstance(t, dict) else t for t in self.active_track.tickets]
else:
self.active_tickets = []
self.active_tickets = []