diff --git a/config.toml b/config.toml index 3c4b96b..6a680f1 100644 --- a/config.toml +++ b/config.toml @@ -1,6 +1,6 @@ [ai] -provider = "minimax" -model = "MiniMax-M2.5" +provider = "gemini" +model = "gemini-2.5-flash-lite" temperature = 0.0 top_p = 1.0 max_tokens = 32000 @@ -24,7 +24,7 @@ bg_shader_enabled = false crt_filter_enabled = false separate_task_dag = false separate_usage_analytics = false -separate_tier1 = false +separate_tier1 = true separate_tier2 = false separate_tier3 = false separate_tier4 = false @@ -36,7 +36,7 @@ separate_external_tools = false "AI Settings" = true "MMA Dashboard" = true "Task DAG" = false -"Usage Analytics" = false +"Usage Analytics" = true "Tier 1" = false "Tier 2" = false "Tier 3" = false diff --git a/project_history.toml b/project_history.toml index 23f419f..d4b492b 100644 --- a/project_history.toml +++ b/project_history.toml @@ -9,5 +9,5 @@ active = "main" [discussions.main] git_commit = "" -last_updated = "2026-05-09T12:24:27" +last_updated = "2026-05-09T14:29:48" history = [] diff --git a/sdm_injection_log.txt b/sdm_injection_log.txt new file mode 100644 index 0000000..a8aa888 Binary files /dev/null and b/sdm_injection_log.txt differ diff --git a/simulation/sim_ai_settings.py b/simulation/sim_ai_settings.py index 7a5de48..06b9add 100644 --- a/simulation/sim_ai_settings.py +++ b/simulation/sim_ai_settings.py @@ -3,6 +3,9 @@ from simulation.sim_base import BaseSimulation, run_sim class AISettingsSimulation(BaseSimulation): def run(self) -> None: + """ + [C: simulation/sim_base.py:run_sim, tests/conftest.py:kill_process_tree, tests/conftest.py:live_gui, tests/test_conductor_abort_event.py:test_conductor_abort_event_populated, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:get_vscode_processes, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_custom_window.py:test_app_window_is_borderless, tests/test_headless_simulation.py:module, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_mock_gemini_cli.py:run_mock, tests/test_orchestration_logic.py:test_conductor_engine_run, tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:test_execution_simulation_run, tests/test_sim_tools.py:test_tools_simulation_run] + """ print("\n--- Running AI Settings Simulation (Gemini Only) ---") # 1. Verify initial model provider = self.client.get_value("current_provider") @@ -28,4 +31,4 @@ class AISettingsSimulation(BaseSimulation): assert final_model == target_model, f"Expected {target_model}, got {final_model}" if __name__ == "__main__": - run_sim(AISettingsSimulation) + run_sim(AISettingsSimulation) \ No newline at end of file diff --git a/simulation/sim_base.py b/simulation/sim_base.py index 2dc155e..c54f97e 100644 --- a/simulation/sim_base.py +++ b/simulation/sim_base.py @@ -62,6 +62,9 @@ class BaseSimulation: self.project_path = None def setup(self, project_name: str = "SimProject") -> None: + """ + [C: simulation/sim_execution.py:ExecutionSimulation.setup, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_sim_base.py:test_base_simulation_setup] + """ print("\n[BaseSim] Connecting to GUI...") if not self.client.wait_for_server(timeout=5): raise RuntimeError("Could not connect to GUI. Ensure it is running with --enable-test-hooks") @@ -88,6 +91,9 @@ class BaseSimulation: time.sleep(1.5) def teardown(self) -> None: + """ + [C: tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live] + """ if self.project_path and os.path.exists(self.project_path): # We keep it for debugging if it failed, but usually we'd clean up # os.remove(self.project_path) @@ -95,9 +101,15 @@ class BaseSimulation: print("[BaseSim] Teardown complete.") def get_value(self, tag: str) -> Any: + """ + [C: simulation/sim_context.py:ContextSimulation.run, simulation/sim_execution.py:ExecutionSimulation.run, simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async, simulation/workflow_sim.py:WorkflowSimulator.wait_for_ai_response, tests/smoke_status_hook.py:test_status_hook, tests/smoke_status_hook.py:wait_for_value, tests/test_auto_switch_sim.py:test_auto_switch_sim, tests/test_deepseek_infra.py:test_gui_provider_list_via_hooks, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_gui2_parity.py:test_gui2_click_hook_works, tests/test_gui2_parity.py:test_gui2_set_value_hook_works, 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_rag_visual_sim.py:test_rag_full_lifecycle_sim, tests/test_rag_visual_sim.py:test_rag_settings_persistence_sim, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_system_prompt_sim.py:test_system_prompt_sim, tests/test_undo_redo_sim.py:test_undo_redo_context_mutation, tests/test_undo_redo_sim.py:test_undo_redo_discussion_mutation, tests/test_undo_redo_sim.py:test_undo_redo_lifecycle, tests/test_visual_mma.py:test_visual_mma_components, tests/test_workspace_profiles_sim.py:test_workspace_profiles_restoration] + """ return self.client.get_value(tag) def wait_for_event(self, event_type: str, timeout: int = 5) -> Optional[dict]: + """ + [C: simulation/sim_execution.py:ExecutionSimulation.run, tests/test_z_negative_flows.py:test_mock_error_result, tests/test_z_negative_flows.py:test_mock_malformed_json, tests/test_z_negative_flows.py:test_mock_timeout] + """ return self.client.wait_for_event(event_type, timeout) def assert_panel_visible(self, panel_tag: str, msg: str = None) -> None: @@ -115,7 +127,10 @@ class BaseSimulation: return False def run_sim(sim_class: type) -> None: - """Helper to run a simulation class standalone.""" + """ + Helper to run a simulation class standalone. + [C: simulation/sim_context.py:module, simulation/sim_execution.py:module, simulation/sim_tools.py:module] + """ sim = sim_class() try: sim.setup() @@ -127,4 +142,4 @@ def run_sim(sim_class: type) -> None: traceback.print_exc() sys.exit(1) finally: - sim.teardown() + sim.teardown() \ No newline at end of file diff --git a/simulation/sim_context.py b/simulation/sim_context.py index 71e158d..2f205e2 100644 --- a/simulation/sim_context.py +++ b/simulation/sim_context.py @@ -4,6 +4,9 @@ from simulation.sim_base import BaseSimulation, run_sim class ContextSimulation(BaseSimulation): def run(self) -> None: + """ + [C: tests/conftest.py:kill_process_tree, tests/conftest.py:live_gui, tests/test_conductor_abort_event.py:test_conductor_abort_event_populated, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:get_vscode_processes, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_custom_window.py:test_app_window_is_borderless, tests/test_headless_simulation.py:module, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_mock_gemini_cli.py:run_mock, tests/test_orchestration_logic.py:test_conductor_engine_run, tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:test_execution_simulation_run, tests/test_sim_tools.py:test_tools_simulation_run] + """ print("\n--- Running Context & Chat Simulation ---") # 1. Skip Discussion Creation, use 'main' print("[Sim] Using existing 'main' discussion") @@ -70,4 +73,4 @@ class ContextSimulation(BaseSimulation): assert len(chat_entries) == 2, f"Expected exactly 2 chat entries after truncation, found {len(chat_entries)}" if __name__ == "__main__": - run_sim(ContextSimulation) + run_sim(ContextSimulation) \ No newline at end of file diff --git a/simulation/sim_execution.py b/simulation/sim_execution.py index 5583789..2ce56aa 100644 --- a/simulation/sim_execution.py +++ b/simulation/sim_execution.py @@ -4,11 +4,17 @@ from simulation.sim_base import BaseSimulation, run_sim class ExecutionSimulation(BaseSimulation): def setup(self, project_name: str = "SimProject") -> None: + """ + [C: tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_sim_base.py:test_base_simulation_setup] + """ super().setup(project_name) if os.path.exists("hello.ps1"): os.remove("hello.ps1") def run(self) -> None: + """ + [C: tests/conftest.py:kill_process_tree, tests/conftest.py:live_gui, tests/test_conductor_abort_event.py:test_conductor_abort_event_populated, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:get_vscode_processes, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_custom_window.py:test_app_window_is_borderless, tests/test_headless_simulation.py:module, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_mock_gemini_cli.py:run_mock, tests/test_orchestration_logic.py:test_conductor_engine_run, tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:test_execution_simulation_run, tests/test_sim_tools.py:test_tools_simulation_run] + """ print("\n--- Running Execution & Modals Simulation ---") # 1. Trigger script generation (Async so we don't block on the wait loop) msg = "Create a hello.ps1 script that prints 'Simulation Test' and execute it." @@ -65,4 +71,4 @@ class ExecutionSimulation(BaseSimulation): print(f"[Sim] Final check: approved {approved_count} scripts.") if __name__ == "__main__": - run_sim(ExecutionSimulation) + run_sim(ExecutionSimulation) \ No newline at end of file diff --git a/simulation/sim_tools.py b/simulation/sim_tools.py index 4720b5e..484b590 100644 --- a/simulation/sim_tools.py +++ b/simulation/sim_tools.py @@ -3,6 +3,9 @@ from simulation.sim_base import BaseSimulation, run_sim class ToolsSimulation(BaseSimulation): def run(self) -> None: + """ + [C: tests/conftest.py:kill_process_tree, tests/conftest.py:live_gui, tests/test_conductor_abort_event.py:test_conductor_abort_event_populated, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:get_vscode_processes, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_custom_window.py:test_app_window_is_borderless, tests/test_headless_simulation.py:module, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_mock_gemini_cli.py:run_mock, tests/test_orchestration_logic.py:test_conductor_engine_run, tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:test_execution_simulation_run, tests/test_sim_tools.py:test_tools_simulation_run] + """ print("\n--- Running Tools Simulation ---") # 1. Trigger list_directory tool msg = "List the files in the current directory." @@ -39,4 +42,4 @@ class ToolsSimulation(BaseSimulation): print(f"[Sim] Final AI Response: {last_ai_msg[:100]}...") if __name__ == "__main__": - run_sim(ToolsSimulation) + run_sim(ToolsSimulation) \ No newline at end of file diff --git a/simulation/user_agent.py b/simulation/user_agent.py index c9cbda4..558f962 100644 --- a/simulation/user_agent.py +++ b/simulation/user_agent.py @@ -22,16 +22,25 @@ class UserSimAgent: return max(0.5, min(5.0, delay)) def wait_to_read(self, text: str) -> None: + """ + [C: simulation/workflow_sim.py:WorkflowSimulator.wait_for_ai_response] + """ if self.enable_delays: delay = self.calculate_reading_delay(text) time.sleep(delay) def wait_to_think(self, probability: float = 0.2, min_delay: float = 2.0, max_delay: float = 5.0) -> None: + """ + [C: simulation/workflow_sim.py:WorkflowSimulator.wait_for_ai_response] + """ if self.enable_delays and random.random() < probability: delay = random.uniform(min_delay, max_delay) time.sleep(delay) def simulate_typing(self, text: str, jitter_range: tuple[float, float] = (0.01, 0.05), batch_typing: bool = False) -> None: + """ + [C: simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async] + """ if not self.enable_delays: return if batch_typing or self.batch_typing: @@ -50,8 +59,10 @@ class UserSimAgent: def generate_response(self, conversation_history: list[dict]) -> str: """ - Generates a human-like response based on the conversation history. - conversation_history: list of dicts with 'role' and 'content' + + Generates a human-like response based on the conversation history. + conversation_history: list of dicts with 'role' and 'content' + [C: simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async] """ last_ai_msg = "" for entry in reversed(conversation_history): @@ -67,9 +78,11 @@ class UserSimAgent: def perform_action_with_delay(self, action_func: Callable, *args: Any, **kwargs: Any) -> Any: """ - Executes an action with a human-like delay if enabled. + + Executes an action with a human-like delay if enabled. + [C: tests/test_user_agent.py:test_perform_action_with_delay] """ if self.enable_delays: delay = random.uniform(0.5, 2.0) time.sleep(delay) - return action_func(*args, **kwargs) + return action_func(*args, **kwargs) \ No newline at end of file diff --git a/simulation/workflow_sim.py b/simulation/workflow_sim.py index 2d16170..d9f46f7 100644 --- a/simulation/workflow_sim.py +++ b/simulation/workflow_sim.py @@ -49,6 +49,9 @@ class WorkflowSimulator: self.user_agent = UserSimAgent(hook_client) def setup_new_project(self, name: str, git_dir: str, project_path: str = None) -> None: + """ + [C: tests/test_workflow_sim.py:test_setup_new_project] + """ print(f"Setting up new project: {name}") if project_path: self.client.click("btn_project_new_automated", user_data=project_path) @@ -62,6 +65,9 @@ class WorkflowSimulator: self.client.set_value("auto_add_history", True) def create_discussion(self, name: str) -> None: + """ + [C: tests/test_workflow_sim.py:test_discussion_switching] + """ print(f"Creating discussion: {name}") self.client.set_value("disc_new_name_input", name) self.client.click("btn_disc_create") @@ -69,6 +75,9 @@ class WorkflowSimulator: time.sleep(2) def switch_discussion(self, name: str) -> None: + """ + [C: tests/test_workflow_sim.py:test_discussion_switching] + """ print(f"Switching to discussion: {name}") self.client.select_list_item("disc_listbox", name) time.sleep(1) @@ -81,6 +90,9 @@ class WorkflowSimulator: time.sleep(1) def truncate_history(self, pairs: int) -> None: + """ + [C: tests/test_workflow_sim.py:test_history_truncation] + """ print(f"Truncating history to {pairs} pairs") self.client.set_value("disc_truncate_pairs", pairs) self.client.click("btn_disc_truncate") @@ -185,4 +197,4 @@ class WorkflowSimulator: print("\nTimeout waiting for AI") active_disc = self.client.get_value("active_discussion") print(f"[DEBUG] Active discussion in GUI at timeout: {active_disc}") - return None + return None \ No newline at end of file diff --git a/src/aggregate.py b/src/aggregate.py index b2fe481..72074f6 100644 --- a/src/aggregate.py +++ b/src/aggregate.py @@ -62,8 +62,9 @@ def resolve_paths(base_dir: Path, entry: str) -> list[Path]: def build_discussion_section(history: list[Any]) -> str: """ - Builds a markdown section for discussion history. - Handles both legacy list[str] and new list[dict]. + + Builds a markdown section for discussion history. + Handles both legacy list[str] and new list[dict]. """ sections = [] for i, entry in enumerate(history, start=1): @@ -78,7 +79,7 @@ def build_discussion_section(history: list[Any]) -> str: def build_files_section(base_dir: Path, files: list[str | dict[str, Any]]) -> str: """ - [C: tests/test_tiered_context.py:test_build_files_section_with_dicts] + [C: tests/test_tiered_context.py:test_build_files_section_with_dicts] """ sections = [] for entry_raw in files: @@ -125,18 +126,19 @@ def build_screenshots_section(base_dir: Path, screenshots: list[str]) -> str: def build_file_items(base_dir: Path, files: list[str | dict[str, Any]]) -> list[dict[str, Any]]: """ - Return a list of dicts describing each file, for use by ai_client when it - wants to upload individual files rather than inline everything as markdown. - - Each dict has: - path : Path (resolved absolute path) - entry : str (original config entry string) - content : str (file text, or error string) - error : bool - mtime : float (last modification time, for skip-if-unchanged optimization) - tier : int | None (optional tier for context management) - auto_aggregate : bool - force_full : bool + + Return a list of dicts describing each file, for use by ai_client when it + wants to upload individual files rather than inline everything as markdown. + + Each dict has: + path : Path (resolved absolute path) + entry : str (original config entry string) + content : str (file text, or error string) + error : bool + mtime : float (last modification time, for skip-if-unchanged optimization) + tier : int | None (optional tier for context management) + auto_aggregate : bool + force_full : bool [C: src/app_controller.py:AppController._bg_task, src/orchestrator_pm.py:module, tests/test_aggregate_flags.py:test_auto_aggregate_skip, tests/test_aggregate_flags.py:test_force_full, tests/test_tiered_context.py:test_build_file_items_with_tiers] """ with get_monitor().scope("build_file_items"): @@ -182,8 +184,9 @@ def build_file_items(base_dir: Path, files: list[str | dict[str, Any]]) -> list[ def build_summary_section(base_dir: Path, files: list[str | dict[str, Any]]) -> str: """ - Build a compact summary section using summarize.py — one short block per file. - Used as the initial block instead of full file contents. + + Build a compact summary section using summarize.py — one short block per file. + Used as the initial block instead of full file contents. """ with get_monitor().scope("build_summary_section"): items = build_file_items(base_dir, files) @@ -191,7 +194,8 @@ def build_summary_section(base_dir: Path, files: list[str | dict[str, Any]]) -> def _build_files_section_from_items(file_items: list[dict[str, Any]]) -> str: """ - Build the files markdown section from pre-read file items (avoids double I/O). + + Build the files markdown section from pre-read file items (avoids double I/O). [C: tests/test_aggregate_flags.py:test_auto_aggregate_skip, tests/test_ui_summary_only_removal.py:test_aggregate_from_items_respects_auto_aggregate] """ sections = [] @@ -212,7 +216,7 @@ def _build_files_section_from_items(file_items: list[dict[str, Any]]) -> str: def build_beads_section(base_dir: Path) -> str: """ - [C: tests/test_aggregate_beads.py:test_build_beads_compaction] + [C: tests/test_aggregate_beads.py:test_build_beads_compaction] """ client = beads_client.BeadsClient(base_dir) if not client.is_initialized(): @@ -261,14 +265,16 @@ def build_markdown_from_items(file_items: list[dict[str, Any]], screenshot_base_ def build_markdown_no_history(file_items: list[dict[str, Any]], screenshot_base_dir: Path, screenshots: list[str], summary_only: bool = False, aggregation_strategy: str = "auto") -> str: """ - Build markdown with only files + screenshots (no history). Used for stable caching. + + Build markdown with only files + screenshots (no history). Used for stable caching. [C: src/app_controller.py:AppController._do_generate, tests/test_history_management.py:test_aggregate_blacklist] """ return build_markdown_from_items(file_items, screenshot_base_dir, screenshots, history=[], summary_only=summary_only, aggregation_strategy=aggregation_strategy) def build_discussion_text(history: list[str]) -> str: """ - Build just the discussion history section text. Returns empty string if no history. + + Build just the discussion history section text. Returns empty string if no history. [C: src/app_controller.py:AppController._do_generate, tests/test_history_management.py:test_aggregate_includes_segregated_history] """ if not history: @@ -278,8 +284,9 @@ def build_discussion_text(history: list[str]) -> str: def build_tier1_context(file_items: list[dict[str, Any]], screenshot_base_dir: Path, screenshots: list[str], history: list[str]) -> str: """ - Tier 1 Context: Strategic/Orchestration. - Full content for core conductor files and files with tier=1, summaries for others. + + Tier 1 Context: Strategic/Orchestration. + Full content for core conductor files and files with tier=1, summaries for others. [C: tests/test_aggregate_flags.py:test_auto_aggregate_skip, tests/test_aggregate_flags.py:test_force_full, tests/test_tiered_context.py:test_build_tier1_context_exists, tests/test_tiered_context.py:test_tiered_context_by_tier_field] """ core_files = {"product.md", "tech-stack.md", "workflow.md", "tracks.md"} @@ -311,8 +318,9 @@ def build_tier1_context(file_items: list[dict[str, Any]], screenshot_base_dir: P def build_tier2_context(file_items: list[dict[str, Any]], screenshot_base_dir: Path, screenshots: list[str], history: list[str]) -> str: """ - Tier 2 Context: Architectural/Tech Lead. - Full content for all files (standard behavior). + + Tier 2 Context: Architectural/Tech Lead. + Full content for all files (standard behavior). [C: tests/test_tiered_context.py:test_build_tier2_context_exists] """ return build_markdown_from_items(file_items, screenshot_base_dir, screenshots, history, summary_only=False) @@ -320,8 +328,9 @@ def build_tier2_context(file_items: list[dict[str, Any]], screenshot_base_dir: P def build_tier3_context(file_items: list[dict[str, Any]], screenshot_base_dir: Path, screenshots: list[str], history: list[str], focus_files: list[str]) -> str: """ - Tier 3 Context: Execution/Worker. - Full content for focus_files and files with tier=3, summaries/skeletons for others. + + Tier 3 Context: Execution/Worker. + Full content for focus_files and files with tier=3, summaries/skeletons for others. [C: tests/test_aggregate_flags.py:test_auto_aggregate_skip, tests/test_aggregate_flags.py:test_force_full, tests/test_perf_aggregate.py:test_build_tier3_context_scaling, tests/test_tiered_context.py:test_build_tier3_context_ast_skeleton, tests/test_tiered_context.py:test_build_tier3_context_exists, tests/test_tiered_context.py:test_tiered_context_by_tier_field] """ with get_monitor().scope("build_tier3_context"): @@ -388,7 +397,7 @@ def build_markdown(base_dir: Path, files: list[str | dict[str, Any]], screenshot def run(config: dict[str, Any], aggregation_strategy: str = "auto") -> tuple[str, Path, list[dict[str, Any]]]: """ - [C: simulation/sim_base.py:run_sim, src/ai_client.py:_send_anthropic, src/ai_client.py:_send_deepseek, src/ai_client.py:_send_gemini, src/ai_client.py:_send_gemini_cli, src/ai_client.py:_send_minimax, src/app_controller.py:AppController._cb_start_track, src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._process_event_queue, src/app_controller.py:AppController._start_track_logic, src/external_editor.py:_find_vscode_in_registry, src/gui_2.py:App._render_snapshot_tab, src/gui_2.py:App.run, src/gui_2.py:main, src/mcp_client.py:get_git_diff, src/project_manager.py:get_git_commit, src/project_manager.py:get_git_log, src/rag_engine.py:RAGEngine._search_mcp, src/shell_runner.py:run_powershell, tests/conftest.py:kill_process_tree, tests/conftest.py:live_gui, tests/test_conductor_abort_event.py:test_conductor_abort_event_populated, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:get_vscode_processes, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_custom_window.py:test_app_window_is_borderless, tests/test_headless_simulation.py:module, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_mock_gemini_cli.py:run_mock, tests/test_orchestration_logic.py:test_conductor_engine_run, tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:test_execution_simulation_run, tests/test_sim_tools.py:test_tools_simulation_run] + [C: simulation/sim_base.py:run_sim, src/ai_client.py:_send_anthropic, src/ai_client.py:_send_deepseek, src/ai_client.py:_send_gemini, src/ai_client.py:_send_gemini_cli, src/ai_client.py:_send_minimax, src/app_controller.py:AppController._cb_start_track, src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._process_event_queue, src/app_controller.py:AppController._start_track_logic, src/external_editor.py:_find_vscode_in_registry, src/gui_2.py:App._render_snapshot_tab, src/gui_2.py:App.run, src/gui_2.py:main, src/mcp_client.py:get_git_diff, src/project_manager.py:get_git_commit, src/project_manager.py:get_git_log, src/rag_engine.py:RAGEngine._search_mcp, src/shell_runner.py:run_powershell, tests/conftest.py:kill_process_tree, tests/conftest.py:live_gui, tests/test_conductor_abort_event.py:test_conductor_abort_event_populated, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:get_vscode_processes, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_custom_window.py:test_app_window_is_borderless, tests/test_headless_simulation.py:module, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_mock_gemini_cli.py:run_mock, tests/test_orchestration_logic.py:test_conductor_engine_run, tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:test_execution_simulation_run, tests/test_sim_tools.py:test_tools_simulation_run] """ namespace = config.get("project", {}).get("name") if not namespace: @@ -414,7 +423,7 @@ def run(config: dict[str, Any], aggregation_strategy: str = "auto") -> tuple[str def main() -> None: # Load global config to find active project """ - [C: simulation/live_walkthrough.py:module, simulation/ping_pong.py:module, src/api_hooks.py:WebSocketServer._run_loop, src/gui_2.py:module, tests/mock_concurrent_mma.py:module, tests/mock_gemini_cli.py:module, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_allow_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_deny_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_unreachable_hook_server, tests/test_cli_tool_bridge.py:module, tests/test_cli_tool_bridge_mapping.py:TestCliToolBridgeMapping.test_mapping_from_api_format, tests/test_cli_tool_bridge_mapping.py:module, tests/test_discussion_takes.py:module, tests/test_external_editor_gui.py:module, tests/test_headless_service.py:TestHeadlessStartup.test_headless_flag_triggers_run, tests/test_headless_service.py:TestHeadlessStartup.test_normal_startup_calls_app_run, tests/test_mma_skeleton.py:module, tests/test_orchestrator_pm.py:module, tests/test_orchestrator_pm_history.py:module, tests/test_post_process.py:module, tests/test_presets.py:module, tests/test_project_serialization.py:module, tests/test_run_worker_lifecycle_abort.py:module, tests/test_symbol_lookup.py:module, tests/test_system_prompt_exposure.py:module, tests/test_theme_nerv_fx.py:module] + [C: simulation/live_walkthrough.py:module, simulation/ping_pong.py:module, src/api_hooks.py:WebSocketServer._run_loop, src/gui_2.py:module, tests/mock_concurrent_mma.py:module, tests/mock_gemini_cli.py:module, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_allow_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_deny_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_unreachable_hook_server, tests/test_cli_tool_bridge.py:module, tests/test_cli_tool_bridge_mapping.py:TestCliToolBridgeMapping.test_mapping_from_api_format, tests/test_cli_tool_bridge_mapping.py:module, tests/test_discussion_takes.py:module, tests/test_external_editor_gui.py:module, tests/test_headless_service.py:TestHeadlessStartup.test_headless_flag_triggers_run, tests/test_headless_service.py:TestHeadlessStartup.test_normal_startup_calls_app_run, tests/test_mma_skeleton.py:module, tests/test_orchestrator_pm.py:module, tests/test_orchestrator_pm_history.py:module, tests/test_post_process.py:module, tests/test_presets.py:module, tests/test_project_serialization.py:module, tests/test_run_worker_lifecycle_abort.py:module, tests/test_symbol_lookup.py:module, tests/test_system_prompt_exposure.py:module, tests/test_theme_nerv_fx.py:module] """ from src.paths import get_config_path config_path = get_config_path() @@ -436,4 +445,4 @@ def main() -> None: print(f"Written: {output_file}") if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/src/ai_client.py b/src/ai_client.py index 7d37858..82dfe67 100644 --- a/src/ai_client.py +++ b/src/ai_client.py @@ -51,7 +51,10 @@ _history_trunc_limit: int = 8000 events: EventEmitter = EventEmitter() def set_model_params(temp: float, max_tok: int, trunc_limit: int = 8000, top_p: float = 1.0) -> None: - """Sets global generation parameters like temperature and max tokens.""" + """ + Sets global generation parameters like temperature and max tokens. + [C: src/app_controller.py:AppController._handle_request_event, src/app_controller.py:AppController.generate, tests/test_history_management.py:test_get_history_bleed_stats_basic] + """ global _temperature, _max_tokens, _history_trunc_limit, _top_p _temperature = temp _max_tokens = max_tok @@ -111,21 +114,33 @@ _local_storage = threading.local() _tool_approval_modes: dict[str, str] = {} def get_current_tier() -> Optional[str]: - """Returns the current tier from thread-local storage.""" + """ + Returns the current tier from thread-local storage. + [C: src/app_controller.py:AppController._on_tool_log, tests/test_ai_client_concurrency.py:intercepted_append] + """ return getattr(_local_storage, "current_tier", None) def set_current_tier(tier: Optional[str]) -> None: - """Sets the current tier in thread-local storage.""" + """ + Sets the current tier in thread-local storage. + [C: src/app_controller.py:AppController._handle_request_event, src/conductor_tech_lead.py:generate_tickets, src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_ai_client_concurrency.py:run_t1, tests/test_ai_client_concurrency.py:run_t2, tests/test_mma_agent_focus_phase1.py:reset_tier, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2] + """ _local_storage.current_tier = tier def get_comms_log_callback() -> Optional[Callable[[dict[str, Any]], None]]: - """Returns the comms log callback (thread-local with global fallback).""" + """ + Returns the comms log callback (thread-local with global fallback). + [C: src/multi_agent_conductor.py:run_worker_lifecycle] + """ tl_cb = getattr(_local_storage, "comms_log_callback", None) if tl_cb: return tl_cb return comms_log_callback def set_comms_log_callback(cb: Optional[Callable[[dict[str, Any]], None]]) -> None: - """Sets the comms log callback (both global and thread-local).""" + """ + Sets the comms log callback (both global and thread-local). + [C: src/app_controller.py:AppController._init_ai_and_hooks, src/multi_agent_conductor.py:run_worker_lifecycle] + """ global comms_log_callback comms_log_callback = cb _local_storage.comms_log_callback = cb @@ -161,19 +176,31 @@ _use_default_base_system_prompt: bool = True _project_context_marker: str = "" def set_custom_system_prompt(prompt: str) -> None: - """Sets a custom system prompt to be combined with the default instructions.""" + """ + Sets a custom system prompt to be combined with the default instructions. + [C: simulation/user_agent.py:UserSimAgent.generate_response, src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event, src/app_controller.py:AppController.generate, src/conductor_tech_lead.py:generate_tickets, src/multi_agent_conductor.py:run_worker_lifecycle, src/orchestrator_pm.py:generate_tracks, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.setUp] + """ global _custom_system_prompt _custom_system_prompt = prompt def set_base_system_prompt(prompt: str) -> None: + """ + [C: src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event, src/app_controller.py:AppController.generate, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.setUp, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_get_combined_respects_use_default, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_set_base_overrides_when_default_false] + """ global _base_system_prompt_override _base_system_prompt_override = prompt def set_use_default_base_prompt(use_default: bool) -> None: + """ + [C: src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event, src/app_controller.py:AppController.generate, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.setUp, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_get_combined_respects_use_default, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_set_base_overrides_when_default_false] + """ global _use_default_base_system_prompt _use_default_base_system_prompt = use_default def set_project_context_marker(marker: str) -> None: + """ + [C: src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event, src/app_controller.py:AppController.generate] + """ global _project_context_marker _project_context_marker = marker @@ -181,6 +208,9 @@ def _get_context_marker() -> str: return _project_context_marker if _project_context_marker.strip() else "[SYSTEM: FILES UPDATED]" def _get_combined_system_prompt(preset: Optional[ToolPreset] = None, bias: Optional[BiasProfile] = None) -> str: + """ + [C: tests/test_bias_efficacy.py:test_bias_efficacy_prompt_generation, tests/test_bias_integration.py:test_system_prompt_biasing, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_get_combined_respects_use_default, tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_ai_client_set_base_overrides_when_default_false] + """ if preset is None: preset = _active_tool_preset if bias is None: bias = _active_bias_profile if _use_default_base_system_prompt: @@ -196,6 +226,9 @@ def _get_combined_system_prompt(preset: Optional[ToolPreset] = None, bias: Optio return base def get_combined_system_prompt(preset: Optional[ToolPreset] = None, bias: Optional[BiasProfile] = None) -> str: + """ + [C: src/app_controller.py:AppController._do_generate, src/app_controller.py:AppController._handle_request_event] + """ return _get_combined_system_prompt(preset, bias) from collections import deque @@ -205,6 +238,9 @@ _comms_log: deque[dict[str, Any]] = deque(maxlen=1000) COMMS_CLAMP_CHARS: int = 300 def _append_comms(direction: str, kind: str, payload: dict[str, Any]) -> None: + """ + [C: tests/test_ai_client_concurrency.py:run_t1, tests/test_ai_client_concurrency.py:run_t2, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2] + """ entry: dict[str, Any] = { "ts": datetime.datetime.now().strftime("%H:%M:%S"), "direction": direction, @@ -221,15 +257,27 @@ def _append_comms(direction: str, kind: str, payload: dict[str, Any]) -> None: _cb(entry) def get_comms_log() -> list[dict[str, Any]]: + """ + [C: src/app_controller.py:AppController._bg_task, src/app_controller.py:AppController._recalculate_session_usage, src/app_controller.py:AppController._start_track_logic, src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2, tests/test_token_usage.py:test_token_usage_tracking] + """ return list(_comms_log) def clear_comms_log() -> None: + """ + [C: src/app_controller.py:AppController._handle_reset_session, src/gui_2.py:App._render_comms_history_panel, src/gui_2.py:App._show_menus, tests/test_ai_client_concurrency.py:test_ai_client_tier_isolation, tests/test_token_usage.py:test_token_usage_tracking] + """ _comms_log.clear() def get_credentials_path() -> Path: + """ + [C: src/mcp_client.py:_is_allowed] + """ return Path(os.environ.get("SLOP_CREDENTIALS", str(Path(__file__).parent.parent / "credentials.toml"))) def _load_credentials() -> dict[str, Any]: + """ + [C: tests/test_deepseek_infra.py:test_credentials_error_mentions_deepseek, tests/test_minimax_provider.py:test_minimax_credentials_template] + """ cred_path = get_credentials_path() try: with open(cred_path, "rb") as f: @@ -247,12 +295,18 @@ def _load_credentials() -> dict[str, Any]: class ProviderError(Exception): def __init__(self, kind: str, provider: str, original: Exception) -> None: + """ + [C: src/api_hooks.py:HookServerInstance.__init__, src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self.kind = kind self.provider = provider self.original = original super().__init__(str(original)) def ui_message(self) -> str: + """ + [C: src/app_controller.py:AppController._handle_request_event, src/app_controller.py:AppController.generate] + """ labels = { "quota": "QUOTA EXHAUSTED", "rate_limit": "RATE LIMITED", @@ -382,7 +436,10 @@ def _classify_minimax_error(exc: Exception) -> ProviderError: return ProviderError("unknown", "minimax", Exception(body)) def set_provider(provider: str, model: str) -> None: - """Updates the active LLM provider and model name.""" + """ + Updates the active LLM provider and model name. + [C: src/app_controller.py:AppController._handle_reset_session, src/app_controller.py:AppController._init_ai_and_hooks, src/app_controller.py:AppController.current_model, src/app_controller.py:AppController.current_provider, src/app_controller.py:AppController.do_fetch, src/multi_agent_conductor.py:run_worker_lifecycle, src/orchestrator_pm.py:generate_tracks, tests/conftest.py:reset_ai_client, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_ai_client_cli.py:test_ai_client_send_gemini_cli, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events, tests/test_deepseek_provider.py:test_deepseek_completion_logic, tests/test_deepseek_provider.py:test_deepseek_model_selection, tests/test_deepseek_provider.py:test_deepseek_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoner_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoning_logic, tests/test_deepseek_provider.py:test_deepseek_streaming, tests/test_deepseek_provider.py:test_deepseek_tool_calling, tests/test_gemini_cli_edge_cases.py:test_gemini_cli_loop_termination, tests/test_gemini_cli_integration.py:test_gemini_cli_full_integration, tests/test_gemini_cli_integration.py:test_gemini_cli_rejection_and_history, tests/test_gemini_cli_parity_regression.py:test_get_history_bleed_stats, tests/test_gemini_cli_parity_regression.py:test_send_invokes_adapter_send, tests/test_gui2_mcp.py:test_mcp_tool_call_is_dispatched, tests/test_history_management.py:test_get_history_bleed_stats_basic, tests/test_minimax_provider.py:test_minimax_default_model, tests/test_minimax_provider.py:test_minimax_history_bleed_stats, tests/test_minimax_provider.py:test_minimax_model_selection, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_rag_integration.py:test_rag_integration, tests/test_tier4_interceptor.py:test_ai_client_passes_qa_callback, tests/test_tier4_interceptor.py:test_gemini_provider_passes_qa_callback_to_run_script, tests/test_token_usage.py:test_token_usage_tracking, tests/test_token_viz.py:test_get_history_bleed_stats_returns_all_keys_unknown_provider] + """ global _provider, _model _provider = provider if provider == "gemini_cli": @@ -401,11 +458,17 @@ def set_provider(provider: str, model: str) -> None: _model = model def get_provider() -> str: - """Returns the current active provider name.""" + """ + Returns the current active provider name. + [C: src/multi_agent_conductor.py:run_worker_lifecycle] + """ return _provider def cleanup() -> None: - """Performs cleanup operations like deleting server-side Gemini caches.""" + """ + Performs cleanup operations like deleting server-side Gemini caches. + [C: src/app_controller.py:AppController.clear_cache, src/app_controller.py:AppController.shutdown, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking_cleanup, tests/test_log_registry.py:TestLogRegistry.tearDown, tests/test_project_serialization.py:TestProjectSerialization.tearDown] + """ global _gemini_client, _gemini_cache, _gemini_cached_file_paths if _gemini_client and _gemini_cache: try: @@ -415,7 +478,10 @@ def cleanup() -> None: _gemini_cached_file_paths = [] def reset_session() -> None: - """Clears conversation history and resets provider-specific session state.""" + """ + Clears conversation history and resets provider-specific session state. + [C: src/app_controller.py:AppController._handle_reset_session, src/app_controller.py:AppController.current_model, src/app_controller.py:AppController.current_provider, src/app_controller.py:AppController.init_state, src/gui_2.py:App._render_provider_panel, src/gui_2.py:App._show_menus, src/multi_agent_conductor.py:run_worker_lifecycle, tests/conftest.py:live_gui, tests/conftest.py:reset_ai_client, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_ai_client_cli.py:test_ai_client_send_gemini_cli, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events, tests/test_deepseek_provider.py:test_deepseek_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoner_payload_verification, tests/test_gemini_cli_integration.py:test_gemini_cli_full_integration, tests/test_gemini_cli_integration.py:test_gemini_cli_rejection_and_history, tests/test_gemini_metrics.py:test_get_gemini_cache_stats_with_mock_client, tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_minimax_provider.py:test_minimax_history_bleed_stats, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2, tests/test_session_logger_reset.py:test_reset_session, tests/test_token_usage.py:test_token_usage_tracking] + """ global _gemini_client, _gemini_chat, _gemini_cache global _gemini_cache_md_hash, _gemini_cache_created_at, _gemini_cached_file_paths global _anthropic_client, _anthropic_history @@ -454,6 +520,9 @@ def reset_session() -> None: file_cache.reset_client() def get_gemini_cache_stats() -> dict[str, Any]: + """ + [C: src/app_controller.py:AppController._recalculate_session_usage, src/app_controller.py:AppController._update_cached_stats, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_gemini_metrics.py:test_get_gemini_cache_stats_with_mock_client] + """ _ensure_gemini_client() if not _gemini_client: return {"cache_count": 0, "total_size_bytes": 0, "cached_files": []} @@ -467,6 +536,9 @@ def get_gemini_cache_stats() -> dict[str, Any]: } def list_models(provider: str) -> list[str]: + """ + [C: src/app_controller.py:AppController.do_fetch, tests/test_agent_capabilities.py:test_agent_capabilities_listing, tests/test_ai_client_list_models.py:test_list_models_gemini_cli, tests/test_deepseek_infra.py:test_deepseek_model_listing, tests/test_minimax_provider.py:test_minimax_list_models] + """ creds = _load_credentials() if provider == "gemini": return _list_gemini_models(creds["gemini"]["api_key"]) @@ -526,14 +598,20 @@ TOOL_NAME: str = "run_powershell" _agent_tools: dict[str, bool] = {} def set_agent_tools(tools: dict[str, bool]) -> None: - """Configures which tools are enabled for the AI agent.""" + """ + Configures which tools are enabled for the AI agent. + [C: src/app_controller.py:AppController._handle_request_event, src/app_controller.py:AppController.generate, tests/test_agent_tools_wiring.py:test_build_anthropic_tools_conversion, tests/test_agent_tools_wiring.py:test_set_agent_tools, tests/test_tool_access_exclusion.py:test_build_anthropic_tools_excludes_disabled, tests/test_tool_access_exclusion.py:test_build_deepseek_tools_excludes_disabled, tests/test_tool_access_exclusion.py:test_gemini_tool_declaration_excludes_disabled, tests/test_tool_access_exclusion.py:test_set_agent_tools_clears_caches] + """ global _agent_tools, _CACHED_ANTHROPIC_TOOLS, _CACHED_DEEPSEEK_TOOLS _agent_tools = tools _CACHED_ANTHROPIC_TOOLS = None _CACHED_DEEPSEEK_TOOLS = None def set_tool_preset(preset_name: Optional[str]) -> None: - """Loads a tool preset and applies it via set_agent_tools.""" + """ + Loads a tool preset and applies it via set_agent_tools. + [C: src/app_controller.py:AppController.init_state, src/gui_2.py:App._render_persona_selector_panel, src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_bias_integration.py:test_set_tool_preset_with_objects, tests/test_tool_preset_env.py:test_tool_preset_env_loading, tests/test_tool_preset_env.py:test_tool_preset_env_no_var, tests/test_tool_presets_execution.py:test_tool_ask_approval, tests/test_tool_presets_execution.py:test_tool_auto_approval, tests/test_tool_presets_execution.py:test_tool_rejection] + """ global _agent_tools, _CACHED_ANTHROPIC_TOOLS, _CACHED_DEEPSEEK_TOOLS, _tool_approval_modes, _active_tool_preset _tool_approval_modes = {} if not preset_name or preset_name == "None": @@ -564,7 +642,10 @@ def set_tool_preset(preset_name: Optional[str]) -> None: _CACHED_DEEPSEEK_TOOLS = None def set_bias_profile(profile_name: Optional[str]) -> None: - """Sets the active tool bias profile for tuning model behavior.""" + """ + Sets the active tool bias profile for tuning model behavior. + [C: src/app_controller.py:AppController.init_state, src/gui_2.py:App._render_agent_tools_panel, src/gui_2.py:App._render_persona_selector_panel, src/multi_agent_conductor.py:run_worker_lifecycle] + """ global _active_bias_profile if not profile_name or profile_name == "None": _active_bias_profile = None @@ -584,6 +665,9 @@ def get_bias_profile() -> Optional[str]: return _active_bias_profile.name if _active_bias_profile else None def _build_anthropic_tools() -> list[dict[str, Any]]: + """ + [C: tests/test_agent_tools_wiring.py:test_build_anthropic_tools_conversion, tests/test_tool_access_exclusion.py:test_build_anthropic_tools_excludes_disabled] + """ raw_tools: list[dict[str, Any]] = [] for spec in mcp_client.get_tool_schemas(): if _agent_tools.get(spec["name"], True): @@ -622,12 +706,18 @@ def _build_anthropic_tools() -> list[dict[str, Any]]: _CACHED_ANTHROPIC_TOOLS: Optional[list[dict[str, Any]]] = None def _get_anthropic_tools() -> list[dict[str, Any]]: + """ + [C: tests/test_bias_efficacy.py:test_bias_efficacy_prompt_generation, tests/test_bias_efficacy.py:test_bias_parameter_nudging, tests/test_bias_integration.py:test_tool_declaration_biasing_anthropic] + """ global _CACHED_ANTHROPIC_TOOLS if _CACHED_ANTHROPIC_TOOLS is None: _CACHED_ANTHROPIC_TOOLS = _build_anthropic_tools() return _CACHED_ANTHROPIC_TOOLS def _gemini_tool_declaration() -> Optional[types.Tool]: + """ + [C: tests/test_tool_access_exclusion.py:test_gemini_tool_declaration_excludes_disabled] + """ raw_tools: list[dict[str, Any]] = [] for spec in mcp_client.get_tool_schemas(): if _agent_tools.get(spec["name"], True): @@ -691,8 +781,10 @@ async def _execute_tool_calls_concurrently( patch_callback: Optional[Callable[[str, str], Optional[str]]] = None ) -> list[tuple[str, str, str, str]]: # tool_name, call_id, output, original_name """ - Executes multiple tool calls concurrently using asyncio.gather. - Returns a list of (tool_name, call_id, output, original_name). + + Executes multiple tool calls concurrently using asyncio.gather. + Returns a list of (tool_name, call_id, output, original_name). + [C: tests/test_async_tools.py:test_execute_tool_calls_concurrently_exception_handling, tests/test_async_tools.py:test_execute_tool_calls_concurrently_timing] """ monitor = performance_monitor.get_monitor() if monitor.enabled: monitor.start_component("ai_client._execute_tool_calls_concurrently") @@ -739,6 +831,9 @@ async def _execute_single_tool_call_async( tier: str | None = None, patch_callback: Optional[Callable[[str, str], Optional[str]]] = None ) -> tuple[str, str, str, str]: + """ + [C: tests/test_external_mcp_e2e.py:test_external_mcp_e2e_refresh_and_call, tests/test_external_mcp_hitl.py:test_external_mcp_hitl_approval, tests/test_external_mcp_hitl.py:test_external_mcp_hitl_rejection, tests/test_tool_presets_execution.py:test_tool_ask_approval, tests/test_tool_presets_execution.py:test_tool_auto_approval, tests/test_tool_presets_execution.py:test_tool_rejection] + """ if tier: set_current_tier(tier) out = "" @@ -870,6 +965,9 @@ def _build_file_diff_text(changed_items: list[dict[str, Any]]) -> str: return "\n\n---\n\n".join(parts) def _build_deepseek_tools() -> list[dict[str, Any]]: + """ + [C: tests/test_tool_access_exclusion.py:test_build_deepseek_tools_excludes_disabled] + """ raw_tools: list[dict[str, Any]] = [] for spec in mcp_client.get_tool_schemas(): if _agent_tools.get(spec["name"], True): @@ -936,6 +1034,9 @@ def _content_block_to_dict(block: Any) -> dict[str, Any]: return {"type": "text", "text": str(block)} def _ensure_gemini_client() -> None: + """ + [C: src/rag_engine.py:GeminiEmbeddingProvider.embed] + """ global _gemini_client if _gemini_client is None: creds = _load_credentials() @@ -959,6 +1060,9 @@ def _send_gemini(md_content: str, user_message: str, base_dir: str, enable_tools: bool = True, stream_callback: Optional[Callable[[str], None]] = None, patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> str: + """ + [C: tests/test_tier4_interceptor.py:test_gemini_provider_passes_qa_callback_to_run_script] + """ global _gemini_chat, _gemini_cache, _gemini_cache_md_hash, _gemini_cache_created_at, _gemini_cached_file_paths monitor = performance_monitor.get_monitor() if monitor.enabled: monitor.start_component("ai_client._send_gemini") @@ -1399,6 +1503,9 @@ def _ensure_anthropic_client() -> None: ) def _chunk_text(text: str, chunk_size: int) -> list[str]: + """ + [C: src/rag_engine.py:RAGEngine._chunk_code, src/rag_engine.py:RAGEngine.index_file] + """ return [text[i:i + chunk_size] for i in range(0, len(text), chunk_size)] def _build_chunked_context_blocks(md_content: str) -> list[dict[str, Any]]: @@ -2144,6 +2251,9 @@ def _send_minimax(md_content: str, user_message: str, base_dir: str, def run_tier4_analysis(stderr: str) -> str: + """ + [C: src/native_orchestrator.py:NativeOrchestrator.analyze_error] + """ if not stderr or not stderr.strip(): return "" try: @@ -2189,6 +2299,9 @@ def run_tier4_patch_callback(stderr: str, base_dir: str) -> Optional[str]: def run_tier4_patch_generation(error: str, file_context: str) -> str: + """ + [C: src/gui_2.py:App.request_patch_from_tier4, src/native_orchestrator.py:NativeOrchestrator.run_tier4_patch, tests/test_tier4_patch_generation.py:test_run_tier4_patch_generation_calls_ai, tests/test_tier4_patch_generation.py:test_run_tier4_patch_generation_empty_error, tests/test_tier4_patch_generation.py:test_run_tier4_patch_generation_returns_diff] + """ if not error or not error.strip(): return "" try: @@ -2215,6 +2328,9 @@ def run_tier4_patch_generation(error: str, file_context: str) -> str: return f"[PATCH GENERATION FAILED] {e}" def get_token_stats(md_content: str) -> dict[str, Any]: + """ + [C: src/app_controller.py:AppController._refresh_api_metrics] + """ global _provider, _gemini_client, _model, _CHARS_PER_TOKEN total_tokens = 0 if _provider == "gemini": @@ -2261,6 +2377,9 @@ def send( patch_callback: Optional[Callable[[str, str], Optional[str]]] = None, rag_engine: Optional[Any] = None, ) -> str: + """ + [C: simulation/user_agent.py:UserSimAgent.generate_response, src/api_hooks.py:WebSocketServer._handler, src/api_hooks.py:WebSocketServer.broadcast, src/app_controller.py:AppController._handle_request_event, src/app_controller.py:AppController.generate, src/conductor_tech_lead.py:generate_tickets, src/multi_agent_conductor.py:run_worker_lifecycle, src/native_orchestrator.py:NativeOrchestrator.execute_ticket, src/orchestrator_pm.py:generate_tracks, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_ai_client_cli.py:test_ai_client_send_gemini_cli, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events, tests/test_deepseek_provider.py:test_deepseek_completion_logic, tests/test_deepseek_provider.py:test_deepseek_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoner_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoning_logic, tests/test_deepseek_provider.py:test_deepseek_streaming, tests/test_deepseek_provider.py:test_deepseek_tool_calling, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_full_flow_integration, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_captures_usage_metadata, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_handles_tool_use_events, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_parses_jsonl_output, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_starts_subprocess_with_correct_args, tests/test_gemini_cli_adapter_parity.py:TestGeminiCliAdapterParity.test_send_parses_tool_calls_from_streaming_json, tests/test_gemini_cli_adapter_parity.py:TestGeminiCliAdapterParity.test_send_starts_subprocess_with_model, tests/test_gemini_cli_edge_cases.py:test_gemini_cli_context_bleed_prevention, tests/test_gemini_cli_edge_cases.py:test_gemini_cli_loop_termination, tests/test_gemini_cli_integration.py:test_gemini_cli_full_integration, tests/test_gemini_cli_integration.py:test_gemini_cli_rejection_and_history, tests/test_gemini_cli_parity_regression.py:test_get_history_bleed_stats, tests/test_gemini_cli_parity_regression.py:test_send_invokes_adapter_send, tests/test_gui2_mcp.py:test_mcp_tool_call_is_dispatched, tests/test_tier4_interceptor.py:test_ai_client_passes_qa_callback, tests/test_token_usage.py:test_token_usage_tracking, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast] + """ monitor = performance_monitor.get_monitor() if monitor.enabled: monitor.start_component("ai_client.send") @@ -2307,6 +2426,9 @@ def send( return res def _add_bleed_derived(d: dict[str, Any], sys_tok: int = 0, tool_tok: int = 0) -> dict[str, Any]: + """ + [C: tests/test_token_viz.py:test_add_bleed_derived_aliases, tests/test_token_viz.py:test_add_bleed_derived_breakdown, tests/test_token_viz.py:test_add_bleed_derived_headroom, tests/test_token_viz.py:test_add_bleed_derived_headroom_clamped_to_zero, tests/test_token_viz.py:test_add_bleed_derived_history_clamped_to_zero, tests/test_token_viz.py:test_add_bleed_derived_would_trim_false, tests/test_token_viz.py:test_add_bleed_derived_would_trim_true, tests/test_token_viz.py:test_would_trim_boundary_exact, tests/test_token_viz.py:test_would_trim_just_above_threshold, tests/test_token_viz.py:test_would_trim_just_below_threshold] + """ cur = d.get("current", 0) lim = d.get("limit", 0) d["estimated_prompt_tokens"] = cur @@ -2326,6 +2448,9 @@ if os.environ.get("SLOP_TOOL_PRESET"): pass def get_history_bleed_stats(md_content: Optional[str] = None) -> dict[str, Any]: + """ + [C: tests/test_gemini_cli_parity_regression.py:test_get_history_bleed_stats, tests/test_history_management.py:test_get_history_bleed_stats_basic, tests/test_minimax_provider.py:test_minimax_history_bleed_stats, tests/test_token_viz.py:test_get_history_bleed_stats_returns_all_keys_unknown_provider] + """ if _provider == "anthropic": with _anthropic_history_lock: history_snapshot = list(_anthropic_history) @@ -2475,7 +2600,10 @@ def get_history_bleed_stats(md_content: Optional[str] = None) -> dict[str, Any]: }) def run_subagent_summarization(file_path: str, content: str, is_code: bool, outline: str) -> str: - """Performs a stateless summarization request using a sub-agent prompt.""" + """ + Performs a stateless summarization request using a sub-agent prompt. + [C: src/summarize.py:summarise_file, tests/test_subagent_summarization.py:test_run_subagent_summarization_anthropic, tests/test_subagent_summarization.py:test_run_subagent_summarization_gemini] + """ prompt_tmpl = mma_prompts.TIER4_SUMMARIZE_CODE_PROMPT if is_code else mma_prompts.TIER4_SUMMARIZE_TEXT_PROMPT prompt = prompt_tmpl.format(file_path=file_path, outline=outline, content=content) if _provider == "gemini": @@ -2522,4 +2650,3 @@ def run_subagent_summarization(file_path: str, content: str, is_code: bool, outl resp_data = adapter.send(prompt, model=_model) return resp_data.get("text", "") return "ERROR: Unsupported provider for sub-agent summarization" - diff --git a/src/api_hook_client.py b/src/api_hook_client.py index f77c170..8465b80 100644 --- a/src/api_hook_client.py +++ b/src/api_hook_client.py @@ -38,11 +38,17 @@ from typing import Any class ApiHookClient: def __init__(self, base_url: str = "http://127.0.0.1:8999", api_key: str | None = None): + """ + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self.base_url = base_url.rstrip('/') self.api_key = api_key def _make_request(self, method: str, path: str, data: dict | None = None, timeout: float = 5.0) -> dict[str, Any] | None: - """Helper to make HTTP requests to the hook server.""" + """ + Helper to make HTTP requests to the hook server. + [C: tests/test_api_hook_client.py:test_unsupported_method_error] + """ url = f"{self.base_url}{path}" headers = {} if self.api_key: @@ -64,7 +70,10 @@ class ApiHookClient: return None def wait_for_server(self, timeout: int = 15) -> bool: - """Polls the health endpoint until the server responds or timeout occurs.""" + """ + Polls the health endpoint until the server responds or timeout occurs. + [C: simulation/live_walkthrough.py:main, simulation/ping_pong.py:main, simulation/sim_base.py:BaseSimulation.setup, tests/smoke_status_hook.py:test_status_hook, 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_auto_switch_sim.py:test_auto_switch_sim, tests/test_conductor_api_hook_integration.py:test_conductor_integrates_api_hook_client_for_verification, tests/test_deepseek_infra.py:test_gui_provider_list_via_hooks, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, 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_gui2_parity.py:test_gui2_click_hook_works, tests/test_gui2_parity.py:test_gui2_custom_callback_hook_works, tests/test_gui2_parity.py:test_gui2_set_value_hook_works, tests/test_gui_context_presets.py:test_gui_context_preset_save_load, tests/test_hooks.py:test_live_hook_server_responses, tests/test_live_workflow.py:test_full_live_workflow, 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:test_mma_step_mode_approval_flow, 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_preset_windows_layout.py:test_api_hook_under_load, tests/test_preset_windows_layout.py:test_preset_windows_opening, 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_rag_visual_sim.py:test_rag_full_lifecycle_sim, tests/test_rag_visual_sim.py:test_rag_settings_persistence_sim, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_system_prompt_sim.py:test_system_prompt_sim, tests/test_tool_management_layout.py:test_tool_management_gettable_fields, tests/test_tool_management_layout.py:test_tool_management_state_updates, tests/test_ui_cache_controls_sim.py:test_ui_cache_controls, tests/test_undo_redo_sim.py:test_undo_redo_context_mutation, tests/test_undo_redo_sim.py:test_undo_redo_discussion_mutation, tests/test_undo_redo_sim.py:test_undo_redo_lifecycle, tests/test_visual_orchestration.py:test_mma_epic_lifecycle, tests/test_visual_sim_gui_ux.py:test_gui_track_creation, tests/test_visual_sim_gui_ux.py:test_gui_ux_event_routing, tests/test_visual_sim_mma_v2.py:test_mma_complete_lifecycle, tests/test_workspace_profiles_sim.py:test_workspace_profiles_restoration, tests/test_z_negative_flows.py:test_mock_error_result, tests/test_z_negative_flows.py:test_mock_malformed_json, tests/test_z_negative_flows.py:test_mock_timeout] + """ start = time.time() while time.time() - start < timeout: status = self.get_status() @@ -74,7 +83,10 @@ class ApiHookClient: return False def get_status(self) -> dict[str, Any]: - """Checks the health of the hook server.""" + """ + Checks the health of the hook server. + [C: tests/test_api_hook_client.py:test_get_status_success, tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_hooks.py:test_live_hook_server_responses, tests/test_mma_concurrent_tracks_stress_sim.py:test_mma_concurrent_tracks_stress, tests/test_preset_windows_layout.py:make_request, tests/test_preset_windows_layout.py:test_preset_windows_opening, tests/test_ui_cache_controls_sim.py:test_ui_cache_controls] + """ res = self._make_request('GET', '/status') if res is None: # For backward compatibility with tests expecting ConnectionError @@ -83,19 +95,31 @@ class ApiHookClient: return res def post_project(self, project_data: dict) -> dict[str, Any]: - """Updates the current project configuration.""" + """ + Updates the current project configuration. + [C: simulation/sim_context.py:ContextSimulation.run] + """ return self._make_request('POST', '/api/project', data=project_data) or {} def get_project(self) -> dict[str, Any]: - """Retrieves the current project state.""" + """ + Retrieves the current project state. + [C: simulation/sim_context.py:ContextSimulation.run, tests/test_api_hook_client.py:test_get_project_success, tests/test_gui_context_presets.py:test_gui_context_preset_save_load, tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_hooks.py:test_live_hook_server_responses, tests/test_live_workflow.py:test_full_live_workflow] + """ return self._make_request('GET', '/api/project') or {} def get_session(self) -> dict[str, Any]: - """Retrieves the current discussion session history.""" + """ + Retrieves the current discussion session history. + [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] + """ return self._make_request('GET', '/api/session') or {} def post_session(self, session_entries: list[dict]) -> dict[str, Any]: - """Updates the session history.""" + """ + Updates the session history. + [C: tests/test_gui_stress_performance.py:test_comms_volume_stress_performance, tests/test_live_workflow.py:test_full_live_workflow] + """ return self._make_request('POST', '/api/session', data={"session": {"entries": session_entries}}) or {} def get_events(self) -> list[dict[str, Any]]: @@ -104,11 +128,17 @@ class ApiHookClient: return res.get("events", []) if res else [] def clear_events(self) -> list[dict[str, Any]]: - """Retrieves and clears the event queue.""" + """ + Retrieves and clears the event queue. + [C: simulation/sim_base.py:BaseSimulation.setup] + """ return self.get_events() def wait_for_event(self, event_type: str, timeout: int = 5) -> dict[str, Any] | None: + """ + [C: simulation/sim_base.py:BaseSimulation.wait_for_event, simulation/sim_execution.py:ExecutionSimulation.run, tests/test_z_negative_flows.py:test_mock_error_result, tests/test_z_negative_flows.py:test_mock_malformed_json, tests/test_z_negative_flows.py:test_mock_timeout] + """ start = time.time() while time.time() - start < timeout: events = self.get_events() @@ -119,35 +149,59 @@ class ApiHookClient: return None def post_gui(self, payload: dict) -> dict[str, Any]: - """Pushes an event to the GUI's AsyncEventQueue via the /api/gui endpoint.""" + """ + Pushes an event to the GUI's AsyncEventQueue via the /api/gui endpoint. + [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] + """ return self._make_request('POST', '/api/gui', data=payload) or {} def push_event(self, action: str, payload: dict) -> dict[str, Any]: - """Convenience to push a GUI task.""" + """ + Convenience to push a GUI task. + [C: tests/test_auto_switch_sim.py:test_auto_switch_sim, tests/test_auto_switch_sim.py:trigger_tier, 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_context_presets.py:test_gui_context_preset_save_load, tests/test_gui_text_viewer.py:test_text_viewer_state_update, 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_preset_windows_layout.py:test_preset_windows_opening, tests/test_saved_presets_sim.py:test_preset_manager_modal, tests/test_saved_presets_sim.py:test_preset_switching, tests/test_tool_management_layout.py:test_tool_management_state_updates, tests/test_tool_presets_sim.py:test_tool_preset_switching, tests/test_visual_mma.py:test_visual_mma_components, tests/test_visual_sim_gui_ux.py:test_gui_ux_event_routing, tests/test_workspace_profiles_sim.py:test_workspace_profiles_restoration, tests/test_z_negative_flows.py:test_mock_error_result, tests/test_z_negative_flows.py:test_mock_malformed_json, tests/test_z_negative_flows.py:test_mock_timeout] + """ return self.post_gui({"action": action, **payload}) def click(self, item: str, user_data: Any = None) -> dict[str, Any]: - """Simulates a button click.""" + """ + Simulates a button click. + [C: simulation/live_walkthrough.py:main, simulation/ping_pong.py:main, simulation/sim_base.py:BaseSimulation.setup, simulation/sim_context.py:ContextSimulation.run, simulation/sim_execution.py:ExecutionSimulation.run, simulation/workflow_sim.py:WorkflowSimulator.create_discussion, simulation/workflow_sim.py:WorkflowSimulator.load_prior_log, simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async, simulation/workflow_sim.py:WorkflowSimulator.setup_new_project, simulation/workflow_sim.py:WorkflowSimulator.truncate_history, simulation/workflow_sim.py:WorkflowSimulator.wait_for_ai_response, tests/test_external_editor_gui.py:test_button_click_is_received, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui2_parity.py:test_gui2_click_hook_works, tests/test_live_workflow.py:test_full_live_workflow, 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:test_mma_step_mode_approval_flow, 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_rag_visual_sim.py:test_rag_full_lifecycle_sim, tests/test_system_prompt_sim.py:test_system_prompt_sim, tests/test_ui_cache_controls_sim.py:test_ui_cache_controls, tests/test_undo_redo_sim.py:test_undo_redo_context_mutation, tests/test_undo_redo_sim.py:test_undo_redo_discussion_mutation, tests/test_undo_redo_sim.py:test_undo_redo_lifecycle, tests/test_visual_orchestration.py:test_mma_epic_lifecycle, tests/test_visual_sim_gui_ux.py:test_gui_track_creation, tests/test_visual_sim_mma_v2.py:_drain_approvals, tests/test_visual_sim_mma_v2.py:test_mma_complete_lifecycle, tests/test_z_negative_flows.py:test_mock_error_result, tests/test_z_negative_flows.py:test_mock_malformed_json, tests/test_z_negative_flows.py:test_mock_timeout] + """ return self.post_gui({"action": "click", "item": item, "user_data": user_data}) def set_value(self, item: str, value: Any) -> dict[str, Any]: - """Sets the value of a GUI widget.""" + """ + Sets the value of a GUI widget. + [C: simulation/live_walkthrough.py:main, simulation/ping_pong.py:main, simulation/sim_ai_settings.py:AISettingsSimulation.run, simulation/sim_base.py:BaseSimulation.setup, simulation/workflow_sim.py:WorkflowSimulator.create_discussion, simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async, simulation/workflow_sim.py:WorkflowSimulator.setup_new_project, simulation/workflow_sim.py:WorkflowSimulator.truncate_history, tests/smoke_status_hook.py:test_status_hook, tests/test_ai_settings_layout.py:test_change_provider_via_hook, tests/test_auto_switch_sim.py:test_auto_switch_sim, tests/test_deepseek_infra.py:test_gui_provider_list_via_hooks, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_gui2_parity.py:test_gui2_click_hook_works, tests/test_gui2_performance.py:test_performance_benchmarking, tests/test_live_gui_integration_v2.py:test_api_gui_state_live, tests/test_live_workflow.py:test_full_live_workflow, 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:test_mma_step_mode_approval_flow, 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_rag_visual_sim.py:test_rag_full_lifecycle_sim, tests/test_rag_visual_sim.py:test_rag_settings_persistence_sim, tests/test_saved_presets_sim.py:test_preset_manager_modal, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_system_prompt_sim.py:test_system_prompt_sim, tests/test_task_dag_popout_sim.py:test_task_dag_popout, tests/test_tool_presets_sim.py:test_tool_preset_switching, tests/test_undo_redo_sim.py:test_undo_redo_context_mutation, tests/test_undo_redo_sim.py:test_undo_redo_discussion_mutation, tests/test_undo_redo_sim.py:test_undo_redo_lifecycle, tests/test_usage_analytics_popout_sim.py:test_usage_analytics_popout, tests/test_visual_orchestration.py:test_mma_epic_lifecycle, tests/test_visual_sim_gui_ux.py:test_gui_track_creation, tests/test_visual_sim_mma_v2.py:test_mma_complete_lifecycle, tests/test_workspace_profiles_sim.py:test_workspace_profiles_restoration, tests/test_z_negative_flows.py:test_mock_error_result, tests/test_z_negative_flows.py:test_mock_malformed_json, tests/test_z_negative_flows.py:test_mock_timeout] + """ return self.post_gui({"action": "set_value", "item": item, "value": value}) def select_tab(self, item: str, value: str) -> dict[str, Any]: - """Selects a specific tab in a tab bar.""" + """ + Selects a specific tab in a tab bar. + [C: simulation/live_walkthrough.py:main, tests/test_api_hook_extensions.py:test_select_tab_integration] + """ return self.set_value(item, value) def select_list_item(self, item: str, value: str) -> dict[str, Any]: - """Selects an item in a listbox or combo.""" + """ + Selects an item in a listbox or combo. + [C: simulation/workflow_sim.py:WorkflowSimulator.create_discussion, simulation/workflow_sim.py:WorkflowSimulator.switch_discussion, tests/test_api_hook_extensions.py:test_select_list_item_integration, tests/test_live_workflow.py:test_full_live_workflow] + """ return self.set_value(item, value) def get_gui_state(self) -> dict[str, Any]: - """Returns the full GUI state available via the hook API.""" + """ + Returns the full GUI state available via the hook API. + [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] + """ return self._make_request('GET', '/api/gui/state') or {} def get_value(self, item: str) -> Any: - """Gets the value of a GUI item via its mapped field.""" + """ + Gets the value of a GUI item via its mapped field. + [C: simulation/sim_ai_settings.py:AISettingsSimulation.run, simulation/sim_base.py:BaseSimulation.get_value, simulation/sim_base.py:BaseSimulation.setup, simulation/sim_base.py:BaseSimulation.wait_for_element, simulation/sim_context.py:ContextSimulation.run, simulation/sim_execution.py:ExecutionSimulation.run, simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async, simulation/workflow_sim.py:WorkflowSimulator.wait_for_ai_response, tests/smoke_status_hook.py:test_status_hook, tests/smoke_status_hook.py:wait_for_value, tests/test_auto_switch_sim.py:test_auto_switch_sim, tests/test_deepseek_infra.py:test_gui_provider_list_via_hooks, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_gui2_parity.py:test_gui2_click_hook_works, tests/test_gui2_parity.py:test_gui2_set_value_hook_works, 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_rag_visual_sim.py:test_rag_full_lifecycle_sim, tests/test_rag_visual_sim.py:test_rag_settings_persistence_sim, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_system_prompt_sim.py:test_system_prompt_sim, tests/test_undo_redo_sim.py:test_undo_redo_context_mutation, tests/test_undo_redo_sim.py:test_undo_redo_discussion_mutation, tests/test_undo_redo_sim.py:test_undo_redo_lifecycle, tests/test_visual_mma.py:test_visual_mma_components, tests/test_workspace_profiles_sim.py:test_workspace_profiles_restoration] + """ # Try state endpoint first (new preferred way) state = self.get_gui_state() if item in state: @@ -168,33 +222,54 @@ class ApiHookClient: return None def get_text_value(self, item_tag: str) -> str | None: - """Wraps get_value and returns its string representation, or None.""" + """ + Wraps get_value and returns its string representation, or None. + [C: tests/test_api_hook_client.py:test_get_text_value] + """ val = self.get_value(item_tag) return str(val) if val is not None else None def get_indicator_state(self, item_tag: str) -> dict[str, bool]: - """Returns the visibility/active state of a status indicator.""" + """ + Returns the visibility/active state of a status indicator. + [C: simulation/live_walkthrough.py:main, tests/test_api_hook_extensions.py:test_get_indicator_state_integration, tests/test_live_workflow.py:test_full_live_workflow] + """ val = self.get_value(item_tag) return {"shown": bool(val)} def get_gui_diagnostics(self) -> dict[str, Any]: - """Retrieves performance and diagnostic metrics.""" + """ + Retrieves performance and diagnostic metrics. + [C: tests/test_api_hook_client.py:test_get_performance_success, tests/test_hooks.py:test_live_hook_server_responses, tests/test_selectable_ui.py:test_selectable_label_stability] + """ return self._make_request('GET', '/api/gui/diagnostics') or {} def get_performance(self) -> dict[str, Any]: - """Retrieves performance metrics from the dedicated endpoint.""" + """ + Retrieves performance metrics from the dedicated endpoint. + [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 self._make_request('GET', '/api/performance') or {} def get_mma_status(self) -> dict[str, Any]: - """Retrieves the dedicated MMA engine status.""" + """ + Retrieves the dedicated MMA engine 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 self._make_request('GET', '/api/gui/mma_status') or {} def get_mma_workers(self) -> dict[str, Any]: - """Retrieves status for all active MMA workers.""" + """ + Retrieves status for all active MMA workers. + [C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_mma_concurrent_tracks_sim.py:test_mma_concurrent_tracks_execution, tests/test_mma_concurrent_tracks_stress_sim.py:_poll_mma_workers] + """ return self._make_request('GET', '/api/mma/workers') or {} def get_context_state(self) -> dict[str, Any]: - """Retrieves the current file and screenshot context state.""" + """ + Retrieves the current file and screenshot context state. + [C: tests/test_gui_context_presets.py:test_gui_context_preset_save_load] + """ return self._make_request('GET', '/api/context/state') or {} def get_financial_metrics(self) -> dict[str, Any]: @@ -206,13 +281,18 @@ class ApiHookClient: return self._make_request('GET', '/api/system/telemetry') or {} def get_node_status(self, node_id: str) -> dict[str, Any]: - """Retrieves status for a specific node in the MMA DAG.""" + """ + Retrieves status for a specific node in the MMA DAG. + [C: tests/test_api_hook_client.py:test_get_node_status] + """ return self._make_request('GET', f'/api/mma/node/{node_id}') or {} def request_confirmation(self, tool_name: str, args: dict) -> bool | None: """ - Pushes a manual confirmation request and waits for response. - Blocks for up to 60 seconds. + + Pushes a manual confirmation request and waits for response. + Blocks for up to 60 seconds. + [C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_sync_hooks.py:test_api_ask_client_error, tests/test_sync_hooks.py:test_api_ask_client_method, tests/test_sync_hooks.py:test_api_ask_client_rejection] """ # Long timeout as this waits for human input (60 seconds) res = self._make_request('POST', '/api/ask', @@ -221,7 +301,10 @@ class ApiHookClient: return res.get('response') if res else None def reset_session(self) -> None: - """Resets the current session via button click.""" + """ + Resets the current session via button click. + [C: src/app_controller.py:AppController._handle_reset_session, src/app_controller.py:AppController.current_model, src/app_controller.py:AppController.current_provider, src/app_controller.py:AppController.init_state, src/gui_2.py:App._render_provider_panel, src/gui_2.py:App._show_menus, src/multi_agent_conductor.py:run_worker_lifecycle, tests/conftest.py:live_gui, tests/conftest.py:reset_ai_client, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_ai_client_cli.py:test_ai_client_send_gemini_cli, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events, tests/test_deepseek_provider.py:test_deepseek_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoner_payload_verification, tests/test_gemini_cli_integration.py:test_gemini_cli_full_integration, tests/test_gemini_cli_integration.py:test_gemini_cli_rejection_and_history, tests/test_gemini_metrics.py:test_get_gemini_cache_stats_with_mock_client, tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation, tests/test_minimax_provider.py:test_minimax_history_bleed_stats, tests/test_mma_agent_focus_phase1.py:test_append_comms_has_source_tier_key, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_none_when_unset, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_set_when_current_tier_set, tests/test_mma_agent_focus_phase1.py:test_append_comms_source_tier_tier2, tests/test_session_logger_reset.py:test_reset_session, tests/test_token_usage.py:test_token_usage_tracking] + """ self.click("btn_reset") def trigger_patch(self, patch_text: str, file_paths: list[str]) -> dict[str, Any]: @@ -232,11 +315,17 @@ class ApiHookClient: }) or {} def apply_patch(self) -> dict[str, Any]: - """Applies the pending patch.""" + """ + Applies the pending patch. + [C: tests/test_patch_modal.py:test_apply_callback] + """ return self._make_request('POST', '/api/patch/apply') or {} def reject_patch(self) -> dict[str, Any]: - """Rejects the pending patch.""" + """ + Rejects the pending patch. + [C: tests/test_patch_modal.py:test_reject_callback, tests/test_patch_modal.py:test_reject_patch] + """ return self._make_request('POST', '/api/patch/reject') or {} def get_patch_status(self) -> dict[str, Any]: @@ -244,7 +333,10 @@ class ApiHookClient: return self._make_request('GET', '/api/patch/status') or {} def spawn_mma_worker(self, data: dict) -> dict: - """Spawns a new MMA worker with the provided configuration.""" + """ + Spawns a new MMA worker with the provided configuration. + [C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation] + """ return self._make_request('POST', '/api/mma/workers/spawn', data=data) or {} def kill_mma_worker(self, worker_id: str) -> dict: @@ -252,7 +344,10 @@ class ApiHookClient: return self._make_request('POST', '/api/mma/workers/kill', data={"worker_id": worker_id}) or {} def pause_mma_pipeline(self) -> dict: - """Pauses the MMA execution pipeline.""" + """ + Pauses the MMA execution pipeline. + [C: tests/test_mma_step_mode_sim.py:test_mma_step_mode_approval_flow] + """ return self._make_request('POST', '/api/mma/pipeline/pause') or {} def resume_mma_pipeline(self) -> dict: @@ -260,14 +355,22 @@ class ApiHookClient: return self._make_request('POST', '/api/mma/pipeline/resume') or {} def inject_context(self, data: dict) -> dict: - """Injects custom file context into the application.""" + """ + Injects custom file context into the application. + [C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation] + """ return self._make_request('POST', '/api/context/inject', data=data) or {} def mutate_mma_dag(self, data: dict) -> dict: - """Mutates the MMA DAG (Directed Acyclic Graph) structure.""" + """ + Mutates the MMA DAG (Directed Acyclic Graph) structure. + [C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation] + """ return self._make_request('POST', '/api/mma/dag/mutate', data=data) or {} def approve_mma_ticket(self, ticket_id: str) -> dict: - """Manually approves a specific ticket for execution in Step Mode.""" + """ + Manually approves a specific ticket for execution in Step Mode. + [C: tests/test_mma_step_mode_sim.py:test_mma_step_mode_approval_flow] + """ return self._make_request('POST', '/api/mma/ticket/approve', data={"ticket_id": ticket_id}) or {} - diff --git a/src/api_hooks.py b/src/api_hooks.py index 918dcf3..0b23f5e 100644 --- a/src/api_hooks.py +++ b/src/api_hooks.py @@ -69,7 +69,10 @@ def _set_app_attr(app: Any, name: str, value: Any) -> None: class HookServerInstance(ThreadingHTTPServer): """Custom HTTPServer that carries a reference to the main App instance.""" def __init__(self, server_address: tuple[str, int], RequestHandlerClass: type, app: Any) -> None: - """Initializes the server instance with an app reference.""" + """ + Initializes the server instance with an app reference. + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ super().__init__(server_address, RequestHandlerClass) self.app = app @@ -410,6 +413,9 @@ class HookHandler(BaseHTTPRequestHandler): event = threading.Event() result = {"status": "done"} def apply_patch(): + """ + [C: tests/test_patch_modal.py:test_apply_callback] + """ try: if hasattr(app, "_apply_pending_patch"): app._apply_pending_patch() @@ -436,6 +442,9 @@ class HookHandler(BaseHTTPRequestHandler): event = threading.Event() result = {"status": "done"} def reject_patch(): + """ + [C: tests/test_patch_modal.py:test_reject_callback, tests/test_patch_modal.py:test_reject_patch] + """ try: app._show_patch_modal = False app._pending_patch_text = None @@ -552,6 +561,9 @@ class HookHandler(BaseHTTPRequestHandler): self.wfile.write(json.dumps({"status": "queued"}).encode("utf-8")) elif self.path == "/api/mma/workers/kill": def kill_worker(): + """ + [C: src/app_controller.py:AppController.kill_worker, src/gui_2.py:App._cb_kill_ticket, tests/test_conductor_engine_abort.py:test_kill_worker_sets_abort_and_joins_thread] + """ try: worker_id = data.get("worker_id") func = _get_app_attr(app, "_kill_worker") @@ -591,6 +603,9 @@ class HookHandler(BaseHTTPRequestHandler): self.wfile.write(json.dumps({"status": "queued"}).encode("utf-8")) elif self.path == "/api/context/inject": def inject_context(): + """ + [C: tests/test_headless_simulation.py:test_mma_track_lifecycle_simulation] + """ files = _get_app_attr(app, "files") if isinstance(files, list): files.extend(data.get("files", [])) @@ -650,6 +665,9 @@ class HookHandler(BaseHTTPRequestHandler): class HookServer: def __init__(self, app: Any, port: int = 8999) -> None: + """ + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self.app = app self.port = port self.server = None @@ -657,6 +675,9 @@ class HookServer: self.websocket_server: WebSocketServer | None = None def start(self) -> None: + """ + [C: src/app_controller.py:AppController._cb_accept_tracks, src/app_controller.py:AppController._cb_plan_epic, src/app_controller.py:AppController._cb_start_track, src/app_controller.py:AppController._fetch_models, src/app_controller.py:AppController._handle_approve_ask, src/app_controller.py:AppController._handle_generate_send, src/app_controller.py:AppController._handle_md_only, src/app_controller.py:AppController._handle_reject_ask, src/app_controller.py:AppController._init_ai_and_hooks, src/app_controller.py:AppController._process_event_queue, src/app_controller.py:AppController._prune_old_logs, src/app_controller.py:AppController._rebuild_rag_index, src/app_controller.py:AppController._run_event_loop, src/app_controller.py:AppController._start_track_logic, src/app_controller.py:AppController.cb_prune_logs, src/app_controller.py:AppController.start_services, src/gui_2.py:App._render_discussion_panel, src/mcp_client.py:ExternalMCPManager.add_server, src/multi_agent_conductor.py:WorkerPool.spawn, src/performance_monitor.py:PerformanceMonitor.__init__, tests/test_ai_client_concurrency.py:test_ai_client_tier_isolation, tests/test_conductor_engine_abort.py:test_kill_worker_sets_abort_and_joins_thread, tests/test_conductor_engine_v2.py:side_effect, tests/test_spawn_interception_v2.py:test_confirm_spawn_pushed_to_queue, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast] + """ if self.thread and self.thread.is_alive(): return is_gemini_cli = _get_app_attr(self.app, 'current_provider', '') == 'gemini_cli' @@ -682,6 +703,9 @@ class HookServer: logging.info(f"Hook server started on port {self.port}") def stop(self) -> None: + """ + [C: src/app_controller.py:AppController.shutdown, src/mcp_client.py:ExternalMCPManager.stop_all, tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast] + """ if self.websocket_server: self.websocket_server.stop() if self.server: @@ -694,6 +718,9 @@ class HookServer: class WebSocketServer: """WebSocket gateway for real-time event streaming.""" def __init__(self, app: Any, port: int = 9000) -> None: + """ + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self.app = app self.port = port self.clients: dict[str, set] = {"events": set(), "telemetry": set()} @@ -726,27 +753,38 @@ class WebSocketServer: asyncio.set_event_loop(self.loop) self._stop_event = asyncio.Event() async def main(): + """ + [C: simulation/live_walkthrough.py:module, simulation/ping_pong.py:module, src/gui_2.py:module, tests/mock_concurrent_mma.py:module, tests/mock_gemini_cli.py:module, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_allow_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_deny_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_unreachable_hook_server, tests/test_cli_tool_bridge.py:module, tests/test_cli_tool_bridge_mapping.py:TestCliToolBridgeMapping.test_mapping_from_api_format, tests/test_cli_tool_bridge_mapping.py:module, tests/test_discussion_takes.py:module, tests/test_external_editor_gui.py:module, tests/test_headless_service.py:TestHeadlessStartup.test_headless_flag_triggers_run, tests/test_headless_service.py:TestHeadlessStartup.test_normal_startup_calls_app_run, tests/test_mma_skeleton.py:module, tests/test_orchestrator_pm.py:module, tests/test_orchestrator_pm_history.py:module, tests/test_post_process.py:module, tests/test_presets.py:module, tests/test_project_serialization.py:module, tests/test_run_worker_lifecycle_abort.py:module, tests/test_symbol_lookup.py:module, tests/test_system_prompt_exposure.py:module, tests/test_theme_nerv_fx.py:module] + """ async with serve(self._handler, "127.0.0.1", self.port) as server: self.server = server await self._stop_event.wait() self.loop.run_until_complete(main()) def start(self) -> None: + """ + [C: src/app_controller.py:AppController._cb_accept_tracks, src/app_controller.py:AppController._cb_plan_epic, src/app_controller.py:AppController._cb_start_track, src/app_controller.py:AppController._fetch_models, src/app_controller.py:AppController._handle_approve_ask, src/app_controller.py:AppController._handle_generate_send, src/app_controller.py:AppController._handle_md_only, src/app_controller.py:AppController._handle_reject_ask, src/app_controller.py:AppController._init_ai_and_hooks, src/app_controller.py:AppController._process_event_queue, src/app_controller.py:AppController._prune_old_logs, src/app_controller.py:AppController._rebuild_rag_index, src/app_controller.py:AppController._run_event_loop, src/app_controller.py:AppController._start_track_logic, src/app_controller.py:AppController.cb_prune_logs, src/app_controller.py:AppController.start_services, src/gui_2.py:App._render_discussion_panel, src/mcp_client.py:ExternalMCPManager.add_server, src/multi_agent_conductor.py:WorkerPool.spawn, src/performance_monitor.py:PerformanceMonitor.__init__, tests/test_ai_client_concurrency.py:test_ai_client_tier_isolation, tests/test_conductor_engine_abort.py:test_kill_worker_sets_abort_and_joins_thread, tests/test_conductor_engine_v2.py:side_effect, tests/test_spawn_interception_v2.py:test_confirm_spawn_pushed_to_queue, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast] + """ if self.thread and self.thread.is_alive(): return self.thread = threading.Thread(target=self._run_loop, daemon=True) self.thread.start() def stop(self) -> None: + """ + [C: src/app_controller.py:AppController.shutdown, src/mcp_client.py:ExternalMCPManager.stop_all, tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast] + """ if self.loop and self._stop_event: self.loop.call_soon_threadsafe(self._stop_event.set) if self.thread: self.thread.join(timeout=2.0) def broadcast(self, channel: str, payload: dict[str, Any]) -> None: + """ + [C: src/app_controller.py:AppController._process_pending_gui_tasks, src/events.py:AsyncEventQueue.put, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast] + """ if not self.loop or channel not in self.clients: return message = json.dumps({"channel": channel, "payload": payload}) for ws in list(self.clients[channel]): asyncio.run_coroutine_threadsafe(ws.send(message), self.loop) - diff --git a/src/app_controller.py b/src/app_controller.py index b8b0f36..85477a2 100644 --- a/src/app_controller.py +++ b/src/app_controller.py @@ -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 = [] \ No newline at end of file diff --git a/src/beads_client.py b/src/beads_client.py index 03775b0..4b528b7 100644 --- a/src/beads_client.py +++ b/src/beads_client.py @@ -12,22 +12,34 @@ class Bead: class BeadsClient: def __init__(self, working_dir: Path): + """ + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self.working_dir = Path(working_dir) self.repo_dir = self.working_dir / ".beads_mock" self.beads_file = self.repo_dir / "beads.json" def init_repo(self) -> None: - """Initialize the mock repository.""" + """ + Initialize the mock repository. + [C: tests/test_aggregate_beads.py:test_build_beads_compaction, tests/test_beads_client.py:test_beads_init_and_query, tests/test_gui_dag_beads.py:test_load_active_tickets_from_beads, tests/test_mcp_client_beads.py:test_bd_mcp_tools] + """ self.repo_dir.mkdir(parents=True, exist_ok=True) if not self.beads_file.exists(): self.beads_file.write_text("[]", encoding="utf-8") def is_initialized(self) -> bool: - """Check if the repository is initialized.""" + """ + Check if the repository is initialized. + [C: src/mcp_client.py:dispatch, tests/test_beads_client.py:test_beads_init_and_query] + """ return self.beads_file.exists() def create_bead(self, title: str, description: str) -> str: - """Create a new bead and return its ID.""" + """ + Create a new bead and return its ID. + [C: src/mcp_client.py:dispatch, tests/test_aggregate_beads.py:test_build_beads_compaction, tests/test_beads_client.py:test_beads_init_and_query, tests/test_gui_dag_beads.py:test_load_active_tickets_from_beads] + """ beads = self._read_beads() bead_id = f"bead-{len(beads) + 1}" bead = {"id": bead_id, "title": title, "description": description, "status": "active"} @@ -36,7 +48,10 @@ class BeadsClient: return bead_id def update_bead(self, bead_id: str, status: str) -> bool: - """Update the status of an existing bead.""" + """ + Update the status of an existing bead. + [C: src/mcp_client.py:dispatch, tests/test_aggregate_beads.py:test_build_beads_compaction, tests/test_beads_client.py:test_beads_init_and_query] + """ beads = self._read_beads() for bead in beads: if bead["id"] == bead_id: @@ -46,7 +61,10 @@ class BeadsClient: return False def list_beads(self) -> List[Bead]: - """List all beads.""" + """ + List all beads. + [C: src/gui_2.py:App._render_beads_tab, src/mcp_client.py:dispatch, tests/test_beads_client.py:test_beads_init_and_query] + """ return [Bead(**b) for b in self._read_beads()] def _read_beads(self) -> List[dict]: @@ -55,4 +73,4 @@ class BeadsClient: return json.loads(self.beads_file.read_text(encoding="utf-8")) def _write_beads(self, beads: List[dict]) -> None: - self.beads_file.write_text(json.dumps(beads, indent=1), encoding="utf-8") + self.beads_file.write_text(json.dumps(beads, indent=1), encoding="utf-8") \ No newline at end of file diff --git a/src/bg_shader.py b/src/bg_shader.py index 80119e1..db21606 100644 --- a/src/bg_shader.py +++ b/src/bg_shader.py @@ -7,11 +7,17 @@ from imgui_bundle import imgui, nanovg as nvg, hello_imgui class BackgroundShader: def __init__(self): + """ + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self.enabled = False self.start_time = time.time() self.ctx: Optional[nvg.Context] = None def render(self, width: float, height: float): + """ + [C: src/gui_2.py:App._gui_func, src/gui_2.py:App._render_discussion_panel, src/gui_2.py:App._render_heavy_text, src/gui_2.py:App._render_markdown_test, src/gui_2.py:App._render_response_panel, src/gui_2.py:App._render_snapshot_tab, src/markdown_helper.py:MarkdownRenderer._render_code_block, src/markdown_helper.py:MarkdownRenderer.render, src/markdown_helper.py:render, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render] + """ if not self.enabled or width <= 0 or height <= 0: return @@ -59,8 +65,10 @@ class BackgroundShader: _bg: Optional[BackgroundShader] = None def get_bg(): + """ + [C: src/gui_2.py:App._gui_func, src/gui_2.py:App._render_theme_panel] + """ global _bg if _bg is None: _bg = BackgroundShader() return _bg - diff --git a/src/conductor_tech_lead.py b/src/conductor_tech_lead.py index ece3734..bf199d4 100644 --- a/src/conductor_tech_lead.py +++ b/src/conductor_tech_lead.py @@ -41,9 +41,11 @@ from typing import Any def generate_tickets(track_brief: str, module_skeletons: str) -> list[dict[str, Any]]: """ - Tier 2 (Tech Lead) call. - Breaks down a Track Brief and module skeletons into discrete Tier 3 Tickets. - """ + + Tier 2 (Tech Lead) call. + Breaks down a Track Brief and module skeletons into discrete Tier 3 Tickets. + [C: src/native_orchestrator.py:NativeOrchestrator.generate_tickets, tests/test_conductor_tech_lead.py:TestConductorTechLead.test_generate_tickets_retry_failure, tests/test_conductor_tech_lead.py:TestConductorTechLead.test_generate_tickets_retry_success, tests/test_conductor_tech_lead.py:TestConductorTechLead.test_generate_tickets_success, tests/test_orchestration_logic.py:test_generate_tickets] + """ # 1. Set Tier 2 Model (Tech Lead - Flash) # 2. Construct Prompt system_prompt = mma_prompts.PROMPTS.get("tier2_sprint_planning") @@ -95,9 +97,11 @@ from src.models import Ticket def topological_sort(tickets: list[dict[str, Any]]) -> list[dict[str, Any]]: """ - Sorts a list of tickets based on their 'depends_on' field. - Raises ValueError if a circular dependency or missing internal dependency is detected. - """ + + Sorts a list of tickets based on their 'depends_on' field. + Raises ValueError if a circular dependency or missing internal dependency is detected. + [C: tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_complex, tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_cycle, tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_empty, tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_linear, tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_missing_dependency, tests/test_conductor_tech_lead.py:test_topological_sort_vlog, tests/test_dag_engine.py:test_topological_sort, tests/test_dag_engine.py:test_topological_sort_cycle, tests/test_orchestration_logic.py:test_topological_sort, tests/test_orchestration_logic.py:test_topological_sort_circular, tests/test_perf_dag.py:test_dag_edge_cases, tests/test_perf_dag.py:test_dag_performance] + """ # 1. Convert to Ticket objects for TrackDAG ticket_objs = [] for t_data in tickets: @@ -118,4 +122,3 @@ if __name__ == "__main__": test_skeletons = "class NewFeature: pass" tickets = generate_tickets(test_brief, test_skeletons) print(json.dumps(tickets, indent=2)) - diff --git a/src/cost_tracker.py b/src/cost_tracker.py index fd5cc0b..5b949ce 100644 --- a/src/cost_tracker.py +++ b/src/cost_tracker.py @@ -46,8 +46,10 @@ MODEL_PRICING = [ def estimate_cost(model: str, input_tokens: int, output_tokens: int) -> float: """ - Estimate the cost of a model call based on input and output tokens. - Returns the total cost in USD. + + Estimate the cost of a model call based on input and output tokens. + Returns the total cost in USD. + [C: src/gui_2.py:App._render_mma_dashboard, src/gui_2.py:App._render_token_budget_panel, tests/test_cost_tracker.py:test_estimate_cost] """ if not model: return 0.0 @@ -59,4 +61,3 @@ def estimate_cost(model: str, input_tokens: int, output_tokens: int) -> float: return input_cost + output_cost return 0.0 - diff --git a/src/dag_engine.py b/src/dag_engine.py index bd96b59..63d1b56 100644 --- a/src/dag_engine.py +++ b/src/dag_engine.py @@ -32,24 +32,29 @@ from src.performance_monitor import get_monitor class TrackDAG: """ - Manages a Directed Acyclic Graph of implementation tickets. - Provides methods for dependency resolution, cycle detection, and topological sorting. - """ + + Manages a Directed Acyclic Graph of implementation tickets. + Provides methods for dependency resolution, cycle detection, and topological sorting. + """ def __init__(self, tickets: List[Ticket]) -> None: """ - Initializes the TrackDAG with a list of Ticket objects. - Args: - tickets: A list of Ticket instances defining the graph nodes and edges. - """ + + Initializes the TrackDAG with a list of Ticket objects. + Args: + tickets: A list of Ticket instances defining the graph nodes and edges. + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self.tickets = tickets self.ticket_map = {t.id: t for t in tickets} def cascade_blocks(self) -> None: """ - Transitively marks `todo` tickets as `blocked` if any dependency is `blocked`. - Propagates 'blocked' status from initially blocked nodes to their dependents. - """ + + Transitively marks `todo` tickets as `blocked` if any dependency is `blocked`. + Propagates 'blocked' status from initially blocked nodes to their dependents. + [C: tests/test_perf_dag.py:test_dag_performance] + """ with get_monitor().scope("dag_cascade_blocks"): # Build adjacency list of dependents using object references to avoid lookups dependents = {t.id: [] for t in self.tickets} @@ -82,10 +87,12 @@ class TrackDAG: def get_ready_tasks(self) -> List[Ticket]: """ - Returns a list of tickets that are in 'todo' status and whose dependencies are all 'completed'. - Returns: - A list of Ticket objects ready for execution. - """ + + Returns a list of tickets that are in 'todo' status and whose dependencies are all 'completed'. + Returns: + A list of Ticket objects ready for execution. + [C: src/models.py:Track.get_executable_tickets, tests/test_dag_engine.py:test_get_ready_tasks_branching, tests/test_dag_engine.py:test_get_ready_tasks_linear, tests/test_dag_engine.py:test_get_ready_tasks_multiple_deps, tests/test_orchestration_logic.py:test_track_executable_tickets] + """ ready = [] for ticket in self.tickets: if ticket.status == 'todo' and self.is_ticket_ready(ticket): @@ -94,10 +101,12 @@ class TrackDAG: def has_cycle(self) -> bool: """ - Performs an iterative Depth-First Search to detect cycles in the dependency graph. - Returns: - True if a cycle is detected, False otherwise. - """ + + Performs an iterative Depth-First Search to detect cycles in the dependency graph. + Returns: + True if a cycle is detected, False otherwise. + [C: src/gui_2.py:App._render_task_dag_panel, tests/test_dag_engine.py:test_has_cycle_complex_no_cycle, tests/test_dag_engine.py:test_has_cycle_direct_cycle, tests/test_dag_engine.py:test_has_cycle_indirect_cycle, tests/test_dag_engine.py:test_has_cycle_no_cycle, tests/test_perf_dag.py:test_dag_edge_cases, tests/test_perf_dag.py:test_dag_performance] + """ with get_monitor().scope("dag_has_cycle"): visited = set() for start_ticket in self.tickets: @@ -125,13 +134,15 @@ class TrackDAG: def topological_sort(self) -> List[str]: """ - Returns a list of ticket IDs in topological order (dependencies before dependents). - Uses Kahn's algorithm for efficient O(V+E) sorting and cycle detection. - Returns: - A list of ticket ID strings. - Raises: - ValueError: If a dependency cycle is detected. - """ + + Returns a list of ticket IDs in topological order (dependencies before dependents). + Uses Kahn's algorithm for efficient O(V+E) sorting and cycle detection. + Returns: + A list of ticket ID strings. + Raises: + ValueError: If a dependency cycle is detected. + [C: tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_complex, tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_cycle, tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_empty, tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_linear, tests/test_conductor_tech_lead.py:TestTopologicalSort.test_topological_sort_missing_dependency, tests/test_conductor_tech_lead.py:test_topological_sort_vlog, tests/test_dag_engine.py:test_topological_sort, tests/test_dag_engine.py:test_topological_sort_cycle, tests/test_orchestration_logic.py:test_topological_sort, tests/test_orchestration_logic.py:test_topological_sort_circular, tests/test_perf_dag.py:test_dag_edge_cases, tests/test_perf_dag.py:test_dag_performance] + """ with get_monitor().scope("dag_topological_sort"): in_degree = {t.id: len(t.depends_on) for t in self.tickets} dependents = {t.id: [] for t in self.tickets} @@ -159,27 +170,32 @@ class TrackDAG: class ExecutionEngine: """ - A state machine that governs the progression of tasks within a TrackDAG. - Handles automatic queueing and manual task approval. - """ + + A state machine that governs the progression of tasks within a TrackDAG. + Handles automatic queueing and manual task approval. + """ def __init__(self, dag: TrackDAG, auto_queue: bool = False) -> None: """ - Initializes the ExecutionEngine. - Args: - dag: The TrackDAG instance to manage. - auto_queue: If True, ready tasks will automatically move to 'in_progress'. - """ + + Initializes the ExecutionEngine. + Args: + dag: The TrackDAG instance to manage. + auto_queue: If True, ready tasks will automatically move to 'in_progress'. + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self.dag = dag self.auto_queue = auto_queue def tick(self) -> List[Ticket]: """ - Evaluates the DAG and returns a list of tasks that are currently 'ready' for execution. - If auto_queue is enabled, tasks without 'step_mode' will be marked as 'in_progress'. - Returns: - A list of ready Ticket objects. - """ + + Evaluates the DAG and returns a list of tasks that are currently 'ready' for execution. + If auto_queue is enabled, tasks without 'step_mode' will be marked as 'in_progress'. + Returns: + A list of ready Ticket objects. + [C: src/multi_agent_conductor.py:ConductorEngine.run, tests/test_arch_boundary_phase3.py:TestArchBoundaryPhase3.test_cascade_blocks_multi_hop, tests/test_arch_boundary_phase3.py:TestArchBoundaryPhase3.test_cascade_blocks_simple, tests/test_arch_boundary_phase3.py:TestArchBoundaryPhase3.test_execution_engine_tick_cascades_blocks, tests/test_arch_boundary_phase3.py:TestArchBoundaryPhase3.test_in_progress_not_blocked, tests/test_arch_boundary_phase3.py:TestArchBoundaryPhase3.test_manual_unblock_restores_todo, tests/test_execution_engine.py:test_execution_engine_auto_queue, tests/test_execution_engine.py:test_execution_engine_basic_flow, tests/test_execution_engine.py:test_execution_engine_step_mode] + """ with get_monitor().scope("dag_tick"): self.dag.cascade_blocks() ready = self.dag.get_ready_tasks() @@ -187,21 +203,25 @@ class ExecutionEngine: def approve_task(self, task_id: str) -> None: """ - Manually transitions a task from 'todo' to 'in_progress' if its dependencies are met. - Args: - task_id: The ID of the task to approve. - """ + + Manually transitions a task from 'todo' to 'in_progress' if its dependencies are met. + Args: + task_id: The ID of the task to approve. + [C: src/multi_agent_conductor.py:ConductorEngine.approve_task, tests/test_execution_engine.py:test_execution_engine_approve_task, tests/test_execution_engine.py:test_execution_engine_step_mode] + """ ticket = self.dag.ticket_map.get(task_id) if ticket and ticket.status == "todo" and self.dag.is_ticket_ready(ticket): ticket.status = "in_progress" def update_task_status(self, task_id: str, status: str) -> None: """ - Force-updates the status of a specific task. - Args: - task_id: The ID of the task. - status: The new status string (e.g., 'todo', 'in_progress', 'completed', 'blocked'). - """ + + Force-updates the status of a specific task. + Args: + task_id: The ID of the task. + status: The new status string (e.g., 'todo', 'in_progress', 'completed', 'blocked'). + [C: src/multi_agent_conductor.py:ConductorEngine.update_task_status, tests/test_arch_boundary_phase3.py:TestArchBoundaryPhase3.test_manual_unblock_restores_todo, tests/test_execution_engine.py:test_execution_engine_auto_queue, tests/test_execution_engine.py:test_execution_engine_basic_flow, tests/test_execution_engine.py:test_execution_engine_status_persistence, tests/test_execution_engine.py:test_execution_engine_update_nonexistent_task] + """ ticket = self.dag.ticket_map.get(task_id) if ticket: - ticket.status = status + ticket.status = status \ No newline at end of file diff --git a/src/diff_viewer.py b/src/diff_viewer.py index ecf8ea8..ce6e033 100644 --- a/src/diff_viewer.py +++ b/src/diff_viewer.py @@ -36,6 +36,9 @@ def parse_diff_header(line: str) -> tuple[Optional[str], Optional[str], Optional return None, None, None def parse_hunk_header(line: str) -> Optional[tuple[int, int, int, int]]: + """ + [C: tests/test_diff_viewer.py:test_parse_hunk_header] + """ if not line.startswith("@@"): return None @@ -57,6 +60,9 @@ def parse_hunk_header(line: str) -> Optional[tuple[int, int, int, int]]: return (old_start, old_count, new_start, new_count) def parse_diff(diff_text: str) -> List[DiffFile]: + """ + [C: src/gui_2.py:App.request_patch_from_tier4, tests/test_diff_viewer.py:test_diff_line_classification, tests/test_diff_viewer.py:test_parse_diff_empty, tests/test_diff_viewer.py:test_parse_diff_none, tests/test_diff_viewer.py:test_parse_diff_with_context, tests/test_diff_viewer.py:test_parse_multiple_files, tests/test_diff_viewer.py:test_parse_simple_diff, tests/test_diff_viewer.py:test_render_diff_text_immediate] + """ if not diff_text or not diff_text.strip(): return [] @@ -132,6 +138,9 @@ def format_diff_for_display(diff_files: List[DiffFile]) -> str: return "\n".join(output) def get_line_color(line: str) -> Optional[str]: + """ + [C: tests/test_diff_viewer.py:test_get_line_color] + """ if line.startswith("+"): return "green" elif line.startswith("-"): @@ -141,6 +150,9 @@ def get_line_color(line: str) -> Optional[str]: return None def render_diff_text_immediate(diff_files: List[DiffFile]) -> List[tuple[str, Optional[str]]]: + """ + [C: tests/test_diff_viewer.py:test_render_diff_text_immediate] + """ output: List[tuple[str, Optional[str]]] = [] for df in diff_files: output.append((f"File: {df.old_path}", "white")) @@ -152,6 +164,9 @@ def render_diff_text_immediate(diff_files: List[DiffFile]) -> List[tuple[str, Op return output def create_backup(file_path: str) -> Optional[str]: + """ + [C: tests/test_diff_viewer.py:test_create_backup, tests/test_diff_viewer.py:test_create_backup_nonexistent] + """ path = Path(file_path) if not path.exists(): return None @@ -160,6 +175,9 @@ def create_backup(file_path: str) -> Optional[str]: return str(backup_path) def apply_patch_to_file(patch_text: str, base_dir: str = ".") -> Tuple[bool, str]: + """ + [C: src/gui_2.py:App._apply_pending_patch, tests/test_diff_viewer.py:test_apply_patch_simple, tests/test_diff_viewer.py:test_apply_patch_with_context] + """ import difflib diff_files = parse_diff(patch_text) @@ -207,6 +225,9 @@ def apply_patch_to_file(patch_text: str, base_dir: str = ".") -> Tuple[bool, str return True, "\n".join(results) def restore_from_backup(file_path: str) -> bool: + """ + [C: tests/test_diff_viewer.py:test_restore_from_backup] + """ backup_path = Path(str(file_path) + ".backup") if not backup_path.exists(): return False @@ -215,6 +236,9 @@ def restore_from_backup(file_path: str) -> bool: return True def cleanup_backup(file_path: str) -> None: + """ + [C: tests/test_diff_viewer.py:test_cleanup_backup] + """ backup_path = Path(str(file_path) + ".backup") if backup_path.exists(): - backup_path.unlink() + backup_path.unlink() \ No newline at end of file diff --git a/src/events.py b/src/events.py index c4e9d15..6e0499e 100644 --- a/src/events.py +++ b/src/events.py @@ -35,20 +35,26 @@ from pathlib import Path class EventEmitter: """ - Simple event emitter for decoupled communication between modules. + + Simple event emitter for decoupled communication between modules. """ def __init__(self) -> None: - """Initializes the EventEmitter with an empty listener map.""" + """ + Initializes the EventEmitter with an empty listener map. + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self._listeners: Dict[str, List[Callable[..., Any]]] = {} def on(self, event_name: str, callback: Callable[..., Any]) -> None: """ - Registers a callback for a specific event. - Args: - event_name: The name of the event to listen for. - callback: The function to call when the event is emitted. + Registers a callback for a specific event. + + Args: + event_name: The name of the event to listen for. + callback: The function to call when the event is emitted. + [C: tests/test_api_events.py:test_event_emission, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events] """ if event_name not in self._listeners: self._listeners[event_name] = [] @@ -56,39 +62,50 @@ class EventEmitter: def emit(self, event_name: str, *args: Any, **kwargs: Any) -> None: """ - Emits an event, calling all registered callbacks. - Args: - event_name: The name of the event to emit. - *args: Positional arguments to pass to callbacks. - **kwargs: Keyword arguments to pass to callbacks. + Emits an event, calling all registered callbacks. + + Args: + event_name: The name of the event to emit. + *args: Positional arguments to pass to callbacks. + **kwargs: Keyword arguments to pass to callbacks. + [C: tests/test_api_events.py:test_event_emission] """ if event_name in self._listeners: for callback in self._listeners[event_name]: callback(*args, **kwargs) def clear(self) -> None: - """Clears all registered listeners.""" + """ + Clears all registered listeners. + [C: src/gui_2.py:App._gui_func, src/gui_2.py:App._render_comms_history_panel, src/gui_2.py:App._render_discussion_panel, src/gui_2.py:App._render_ticket_queue, src/gui_2.py:App._render_tool_calls_panel, src/gui_2.py:App._show_menus, src/history.py:HistoryManager.push, src/markdown_helper.py:MarkdownRenderer._render_code_block, src/markdown_helper.py:MarkdownRenderer.clear_cache, src/multi_agent_conductor.py:ConductorEngine.resume, src/multi_agent_conductor.py:WorkerPool.join_all, src/paths.py:reset_resolved, src/summary_cache.py:SummaryCache.clear, tests/conftest.py:reset_ai_client] + """ self._listeners.clear() class AsyncEventQueue: """ - Synchronous event queue for decoupled communication using queue.Queue. - (Named AsyncEventQueue for architectural consistency, but is synchronous) + + Synchronous event queue for decoupled communication using queue.Queue. + (Named AsyncEventQueue for architectural consistency, but is synchronous) """ def __init__(self) -> None: - """Initializes the AsyncEventQueue with an internal queue.Queue.""" + """ + Initializes the AsyncEventQueue with an internal queue.Queue. + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self._queue: queue.Queue[Tuple[str, Any]] = queue.Queue() self.websocket_server: Optional[Any] = None def put(self, event_name: str, payload: Any = None) -> None: """ - Puts an event into the queue. - Args: - event_name: The name of the event. - payload: Optional data associated with the event. + Puts an event into the queue. + + Args: + event_name: The name of the event. + payload: Optional data associated with the event. + [C: src/gui_2.py:App._render_external_tools_panel, src/gui_2.py:App._render_log_management, src/gui_2.py:App._render_rag_panel, src/gui_2.py:App._render_token_budget_panel, src/gui_2.py:App._show_menus, src/multi_agent_conductor.py:ConductorEngine._push_state, src/multi_agent_conductor.py:_queue_put, tests/test_gui_events_v2.py:test_sync_event_queue, tests/test_sync_events.py:test_sync_event_queue_multiple, tests/test_sync_events.py:test_sync_event_queue_none_payload, tests/test_sync_events.py:test_sync_event_queue_put_get] """ self._queue.put((event_name, payload)) if self.websocket_server: @@ -103,19 +120,23 @@ class AsyncEventQueue: def get(self) -> Tuple[str, Any]: """ - Gets an event from the queue. - Returns: - A tuple containing (event_name, payload). + Gets an event from the queue. + + Returns: + A tuple containing (event_name, payload). + [C: simulation/live_walkthrough.py:main, simulation/ping_pong.py:main, simulation/sim_context.py:ContextSimulation.run, simulation/sim_execution.py:ExecutionSimulation.run, simulation/sim_tools.py:ToolsSimulation.run, simulation/user_agent.py:UserSimAgent.generate_response, simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async, simulation/workflow_sim.py:WorkflowSimulator.wait_for_ai_response, src/external_editor.py:ExternalEditorLauncher.get_editor, src/external_editor.py:get_default_launcher, src/external_editor.py:resolve_project_editor_override, src/gemini_cli_adapter.py:GeminiCliAdapter.send, src/gui_2.py:App.__init__, src/gui_2.py:App._cb_block_ticket, src/gui_2.py:App._cb_unblock_ticket, src/gui_2.py:App._gui_func, src/gui_2.py:App._handle_history_logic, src/gui_2.py:App._render_cache_panel, src/gui_2.py:App._render_comms_history_panel, src/gui_2.py:App._render_context_composition_panel, src/gui_2.py:App._render_context_presets_panel, src/gui_2.py:App._render_diagnostics_panel, src/gui_2.py:App._render_discussion_panel, src/gui_2.py:App._render_external_editor_panel, src/gui_2.py:App._render_external_tools_panel, src/gui_2.py:App._render_history_window, src/gui_2.py:App._render_log_management, src/gui_2.py:App._render_mma_dashboard, src/gui_2.py:App._render_persona_editor_window, src/gui_2.py:App._render_persona_selector_panel, src/gui_2.py:App._render_projects_panel, src/gui_2.py:App._render_session_insights_panel, src/gui_2.py:App._render_shader_live_editor, src/gui_2.py:App._render_snapshot_tab, src/gui_2.py:App._render_synthesis_panel, src/gui_2.py:App._render_takes_panel, src/gui_2.py:App._render_task_dag_panel, src/gui_2.py:App._render_thinking_trace, src/gui_2.py:App._render_ticket_queue, src/gui_2.py:App._render_tier_stream_panel, src/gui_2.py:App._render_token_budget_panel, src/gui_2.py:App._render_tool_analytics_panel, src/gui_2.py:App._render_tool_calls_panel, src/gui_2.py:App._render_tool_preset_manager_content, src/gui_2.py:App._render_track_proposal_modal, 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, src/gui_2.py:App.load_context_preset, src/gui_2.py:App.render_path_field, src/gui_2.py:App.run, src/gui_2.py:truncate_entries, src/history.py:UISnapshot.from_dict, src/log_registry.py:LogRegistry.get_old_non_whitelisted_sessions, src/log_registry.py:LogRegistry.is_session_whitelisted, src/log_registry.py:LogRegistry.update_auto_whitelist_status, src/log_registry.py:LogRegistry.update_session_metadata, src/markdown_helper.py:MarkdownRenderer._render_code_block, src/mcp_client.py:StdioMCPServer._send_request, src/mcp_client.py:StdioMCPServer.call_tool, src/mcp_client.py:_DDGParser.handle_starttag, src/mcp_client.py:configure, src/mcp_client.py:dispatch, src/mcp_client.py:get_tool_schemas, src/models.py:BiasProfile.from_dict, src/models.py:ExternalEditorConfig.from_dict, src/models.py:FileItem.from_dict, src/models.py:MCPConfiguration.from_dict, src/models.py:MCPServerConfig.from_dict, src/models.py:Metadata.from_dict, src/models.py:Persona.from_dict, src/models.py:Persona.max_output_tokens, src/models.py:Persona.model, src/models.py:Persona.provider, src/models.py:Persona.temperature, src/models.py:Persona.top_p, src/models.py:Preset.from_dict, src/models.py:RAGConfig.from_dict, src/models.py:TextEditorConfig.from_dict, src/models.py:Ticket.from_dict, src/models.py:Tool.from_dict, src/models.py:ToolPreset.from_dict, src/models.py:Track.from_dict, src/models.py:TrackState.from_dict, src/models.py:VectorStoreConfig.from_dict, src/models.py:WorkspaceProfile.from_dict, src/models.py:save_config, src/multi_agent_conductor.py:ConductorEngine.__init__, src/multi_agent_conductor.py:ConductorEngine.kill_worker, src/multi_agent_conductor.py:ConductorEngine.parse_json_tickets, src/multi_agent_conductor.py:ConductorEngine.run, src/multi_agent_conductor.py:clutch_callback, src/multi_agent_conductor.py:confirm_spawn, src/multi_agent_conductor.py:run_worker_lifecycle, src/multi_agent_conductor.py:worker_comms_callback, src/native_orchestrator.py:NativeOrchestrator.execute_ticket, src/orchestrator_pm.py:generate_tracks, src/orchestrator_pm.py:get_track_history_summary, src/orchestrator_pm.py:module, src/paths.py:_get_project_conductor_dir_from_toml, src/paths.py:get_config_path, src/paths.py:get_global_personas_path, src/paths.py:get_global_presets_path, src/paths.py:get_global_tool_presets_path, src/paths.py:get_global_workspace_profiles_path, src/performance_monitor.py:PerformanceMonitor._get_avg, src/performance_monitor.py:PerformanceMonitor.end_component, src/performance_monitor.py:PerformanceMonitor.get_metrics, src/personas.py:PersonaManager.get_persona_scope, src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.delete_preset, src/presets.py:PresetManager.get_preset_scope, src/presets.py:PresetManager.load_all, src/project_manager.py:branch_discussion, src/project_manager.py:entry_to_str, src/project_manager.py:flat_config, src/project_manager.py:get_all_tracks, src/project_manager.py:load_track_history, src/project_manager.py:migrate_from_legacy_config, src/project_manager.py:promote_take, src/rag_engine.py:RAGEngine.get_all_indexed_paths, src/rag_engine.py:RAGEngine.index_file, src/shell_runner.py:_build_subprocess_env, src/shell_runner.py:_load_env_config, src/summarize.py:build_summary_markdown, src/summarize.py:summarise_file, src/summarize.py:summarise_items, src/summary_cache.py:SummaryCache.get_summary, src/synthesis_formatter.py:format_takes_diff, src/theme.py:apply, src/theme.py:get_palette_colours, src/theme.py:get_shader_config, src/theme.py:get_window_frame_config, src/theme.py:load_from_config, src/theme_2.py:load_from_config, src/tool_bias.py:ToolBiasEngine.apply_semantic_nudges, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/conftest.py:live_gui, tests/mock_gemini_cli.py:main, 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_async_tools.py:mocked_async_dispatch, tests/test_auto_switch_sim.py:test_auto_switch_sim, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_allow_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_deny_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_unreachable_hook_server, tests/test_cli_tool_bridge_mapping.py:TestCliToolBridgeMapping.test_mapping_from_api_format, tests/test_conductor_api_hook_integration.py:simulate_conductor_phase_completion, tests/test_conductor_engine_v2.py:mock_open_side_effect, tests/test_conductor_engine_v2.py:mock_send_side_effect, 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_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_captures_usage_metadata, tests/test_gui2_performance.py:test_performance_benchmarking, tests/test_gui_context_presets.py:test_gui_context_preset_save_load, tests/test_gui_events_v2.py:test_sync_event_queue, tests/test_gui_performance_requirements.py:test_idle_performance_requirements, tests/test_gui_phase4.py:test_delete_ticket_logic, tests/test_gui_stress_performance.py:test_comms_volume_stress_performance, tests/test_gui_text_viewer.py:test_text_viewer_state_update, tests/test_gui_updates.py:test_gui_updates_on_event, tests/test_headless_service.py:TestHeadlessAPI.test_endpoint_no_api_key_configured, tests/test_headless_service.py:TestHeadlessAPI.test_get_context_endpoint, tests/test_headless_service.py:TestHeadlessAPI.test_health_endpoint, tests/test_headless_service.py:TestHeadlessAPI.test_list_sessions_endpoint, tests/test_headless_service.py:TestHeadlessAPI.test_pending_actions_endpoint, tests/test_headless_service.py:TestHeadlessAPI.test_status_endpoint_authorized, tests/test_headless_service.py:TestHeadlessAPI.test_status_endpoint_unauthorized, tests/test_live_gui_integration_v2.py:test_api_gui_state_live, 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_live_workflow.py:test_full_live_workflow, tests/test_live_workflow.py:wait_for_value, tests/test_log_registry.py:TestLogRegistry.test_register_session, tests/test_log_registry.py:TestLogRegistry.test_update_session_metadata, tests/test_mma_agent_focus_phase3.py:test_comms_log_filter_not_applied_for_prior_session, tests/test_mma_agent_focus_phase3.py:test_comms_log_filter_tier3_only, tests/test_mma_agent_focus_phase3.py:test_tool_log_filter_all, tests/test_mma_agent_focus_phase3.py:test_tool_log_filter_excludes_none_tier, tests/test_mma_agent_focus_phase3.py:test_tool_log_filter_tier3_only, tests/test_mma_approval_indicators.py:_make_app, tests/test_mma_concurrent_tracks_sim.py:test_mma_concurrent_tracks_execution, tests/test_mma_concurrent_tracks_stress_sim.py:_poll_mma_workers, tests/test_mma_concurrent_tracks_stress_sim.py:test_mma_concurrent_tracks_stress, tests/test_mma_dashboard_streams.py:_make_app, tests/test_mma_orchestration_gui.py:test_handle_ai_response_with_stream_id, 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_mock_gemini_cli.py:get_message_content, 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_per_ticket_model.py:test_model_override_serialization, tests/test_phase6_engine.py:test_worker_streaming_intermediate, tests/test_preset_windows_layout.py:test_api_hook_under_load, tests/test_preset_windows_layout.py:test_preset_windows_opening, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, 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_switching, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_sim_ai_settings.py:side_effect, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:side_effect, tests/test_spawn_interception_v2.py:test_confirm_spawn_pushed_to_queue, tests/test_status_encapsulation.py:test_status_properties, tests/test_sync_events.py:test_sync_event_queue_multiple, tests/test_sync_events.py:test_sync_event_queue_none_payload, tests/test_sync_events.py:test_sync_event_queue_put_get, tests/test_task_dag_popout_sim.py:test_task_dag_popout, tests/test_tier4_interceptor.py:test_ai_client_passes_qa_callback, tests/test_tiered_aggregation.py:test_app_controller_do_generate_uses_persona_strategy, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_token_usage.py:test_token_usage_tracking, tests/test_tool_management_layout.py:test_tool_management_state_updates, tests/test_tool_preset_env.py:test_tool_preset_env_loading, tests/test_tool_preset_env.py:test_tool_preset_env_no_var, tests/test_tool_presets_sim.py:test_tool_preset_switching, tests/test_ui_cache_controls_sim.py:test_ui_cache_controls, tests/test_ui_summary_only_removal.py:test_project_without_summary_only_loads, tests/test_usage_analytics_popout_sim.py:test_usage_analytics_popout, tests/test_visual_orchestration.py:test_mma_epic_lifecycle, tests/test_visual_sim_gui_ux.py:test_gui_track_creation, tests/test_visual_sim_gui_ux.py:test_gui_ux_event_routing, tests/test_visual_sim_mma_v2.py:_drain_approvals, tests/test_visual_sim_mma_v2.py:_mma_active, tests/test_visual_sim_mma_v2.py:_poll, tests/test_visual_sim_mma_v2.py:_tier3_in_streams, tests/test_visual_sim_mma_v2.py:_tier3_usage_nonzero, tests/test_visual_sim_mma_v2.py:_track_loaded, tests/test_visual_sim_mma_v2.py:test_mma_complete_lifecycle] """ return self._queue.get() def empty(self) -> bool: """ - Checks if the queue is empty. - Returns: - True if the queue is empty, False otherwise. + Checks if the queue is empty. + + Returns: + True if the queue is empty, False otherwise. + [C: 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] """ return self._queue.empty() @@ -124,7 +145,10 @@ class AsyncEventQueue: self._queue.task_done() def join(self) -> None: - """Blocks until all items in the queue have been gotten and processed.""" + """ + Blocks until all items in the queue have been gotten and processed. + [C: simulation/live_walkthrough.py:main, simulation/ping_pong.py:module, simulation/sim_base.py:module, src/file_cache.py:ASTParser.get_code_outline, src/gemini_cli_adapter.py:GeminiCliAdapter.count_tokens, src/gemini_cli_adapter.py:GeminiCliAdapter.send, src/gui_2.py:App._render_beads_tab, src/gui_2.py:App._render_discussion_panel, src/gui_2.py:App._render_mma_dashboard, src/log_pruner.py:LogPruner.prune, src/log_registry.py:LogRegistry.update_auto_whitelist_status, src/markdown_helper.py:MarkdownRenderer._render_code_block, src/mcp_client.py:StdioMCPServer.call_tool, src/mcp_client.py:_resolve_and_check, src/mcp_client.py:derive_code_path, src/mcp_client.py:dispatch, src/mcp_client.py:fetch_url, src/mcp_client.py:get_file_slice, src/mcp_client.py:get_tree, src/mcp_client.py:list_directory, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_class_summary, src/mcp_client.py:py_get_definition, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:py_get_imports, src/mcp_client.py:py_get_signature, src/mcp_client.py:py_get_symbol_info, src/mcp_client.py:py_get_var_declaration, src/mcp_client.py:search_files, src/mcp_client.py:set_file_slice, src/mcp_client.py:web_search, src/models.py:parse_history_entries, src/multi_agent_conductor.py:ConductorEngine.kill_worker, src/multi_agent_conductor.py:WorkerPool.join_all, src/orchestrator_pm.py:generate_tracks, src/orchestrator_pm.py:get_track_history_summary, src/outline_tool.py:CodeOutliner.outline, src/performance_monitor.py:PerformanceMonitor.stop, src/project_manager.py:str_to_entry, src/rag_engine.py:RAGEngine._init_vector_store, src/rag_engine.py:RAGEngine.index_file, src/session_logger.py:open_session, src/shell_runner.py:_build_subprocess_env, src/shell_runner.py:run_powershell, src/summarize.py:_summarise_generic, src/summarize.py:_summarise_markdown, src/summarize.py:_summarise_python, src/summarize.py:_summarise_toml, src/summarize.py:build_summary_markdown, src/synthesis_formatter.py:format_takes_diff, src/tool_bias.py:ToolBiasEngine.generate_tooling_strategy, tests/conftest.py:live_gui, tests/conftest.py:module, tests/mock_gemini_cli.py:main, tests/smoke_status_hook.py:module, tests/test_agent_capabilities.py:module, tests/test_agent_tools_wiring.py:module, tests/test_ai_client_concurrency.py:test_ai_client_tier_isolation, tests/test_api_hook_client.py:module, tests/test_api_hook_extensions.py:module, tests/test_arch_boundary_phase1.py:module, tests/test_arch_boundary_phase2.py:module, tests/test_arch_boundary_phase3.py:module, tests/test_auto_switch_sim.py:module, tests/test_cli_tool_bridge.py:module, tests/test_cli_tool_bridge_mapping.py:module, tests/test_context_pruner.py:test_performance_large_file, tests/test_context_pruner.py:test_token_reduction_logging, tests/test_deepseek_infra.py:module, tests/test_extended_sims.py:module, tests/test_external_editor_gui.py:module, tests/test_gemini_metrics.py:module, tests/test_gui2_parity.py:module, tests/test_gui2_performance.py:module, tests/test_gui_diagnostics.py:module, tests/test_gui_performance_requirements.py:module, tests/test_gui_stress_performance.py:module, tests/test_gui_updates.py:module, tests/test_headless_service.py:module, tests/test_history_management.py:module, tests/test_hooks.py:module, tests/test_layout_reorganization.py:module, tests/test_live_workflow.py:module, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_handles_relative_paths_starting_with_logs, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_removes_empty_sessions_regardless_of_age, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_removes_sessions_without_metadata_regardless_of_age, tests/test_log_registry.py:TestLogRegistry.setUp, tests/test_mcp_perf_tool.py:module, tests/test_mcp_ts_integration.py:module, tests/test_mma_approval_indicators.py:_collect_text_colored_args, tests/test_mma_concurrent_tracks_sim.py:module, tests/test_mma_concurrent_tracks_stress_sim.py:module, tests/test_mma_dashboard_streams.py:TestMMADashboardStreams.test_tier3_renders_worker_subheaders, tests/test_mma_step_mode_sim.py:module, tests/test_patch_modal_gui.py:module, tests/test_performance_monitor.py:module, tests/test_rag_integration.py:mock_project, tests/test_rag_integration.py:test_rag_integration, tests/test_rag_phase4_final_verify.py:module, tests/test_rag_phase4_final_verify.py:test_phase4_final_verify, tests/test_rag_phase4_stress.py:module, tests/test_rag_phase4_stress.py:test_rag_large_codebase_verification_sim, tests/test_rag_visual_sim.py:module, tests/test_selectable_ui.py:module, tests/test_sim_ai_settings.py:module, tests/test_sim_base.py:module, tests/test_sim_context.py:module, tests/test_sim_execution.py:module, tests/test_sim_tools.py:module, tests/test_skeleton_injection.py:test_update_inject_preview_truncation, tests/test_spawn_interception_v2.py:test_confirm_spawn_pushed_to_queue, tests/test_system_prompt_exposure.py:module, tests/test_system_prompt_sim.py:module, tests/test_token_usage.py:module, tests/test_tool_management_layout.py:module, tests/test_ts_c_tools.py:module, tests/test_ts_cpp_tools.py:module, tests/test_ui_cache_controls_sim.py:module, tests/test_undo_redo_sim.py:module, tests/test_user_agent.py:module, tests/test_visual_orchestration.py:module, tests/test_visual_sim_gui_ux.py:module, tests/test_visual_sim_gui_ux.py:test_gui_track_creation, tests/test_visual_sim_mma_v2.py:module, tests/test_workflow_sim.py:module, tests/test_workspace_profiles_sim.py:module] + """ self._queue.join() # Alias for backward compatibility @@ -132,10 +156,14 @@ SyncEventQueue = AsyncEventQueue class UserRequestEvent: """ - Payload for a user request event. + + Payload for a user request event. """ def __init__(self, prompt: str, stable_md: str, file_items: List[Any], disc_text: str, base_dir: str) -> None: + """ + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self.prompt = prompt self.stable_md = stable_md self.file_items = file_items @@ -143,6 +171,9 @@ class UserRequestEvent: self.base_dir = base_dir def to_dict(self) -> Dict[str, Any]: + """ + [C: src/gui_2.py:App._take_snapshot, src/gui_2.py:App.save_context_preset, src/models.py:ExternalEditorConfig.to_dict, src/models.py:MCPConfiguration.to_dict, src/models.py:RAGConfig.to_dict, src/models.py:ToolPreset.to_dict, src/models.py:Track.to_dict, src/models.py:TrackState.to_dict, src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ def _make_serializable(obj: Any) -> Any: if isinstance(obj, dict): return {k: _make_serializable(v) for k, v in obj.items()} @@ -162,4 +193,4 @@ class UserRequestEvent: "file_items": _make_serializable(self.file_items), "disc_text": self.disc_text, "base_dir": str(self.base_dir) - } + } \ No newline at end of file diff --git a/src/external_editor.py b/src/external_editor.py index 7d6dcca..f8e1f8e 100644 --- a/src/external_editor.py +++ b/src/external_editor.py @@ -12,9 +12,15 @@ from src.models import ExternalEditorConfig, TextEditorConfig class ExternalEditorLauncher: def __init__(self, config: ExternalEditorConfig): + """ + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self.config = config def get_editor(self, editor_name: Optional[str] = None) -> Optional[TextEditorConfig]: + """ + [C: tests/test_external_editor.py:TestExternalEditorLauncher.test_get_editor_by_name, tests/test_external_editor.py:TestExternalEditorLauncher.test_get_editor_returns_default, tests/test_external_editor.py:TestExternalEditorLauncher.test_get_editor_unknown_name] + """ if editor_name: return self.config.editors.get(editor_name) return self.config.get_default() @@ -22,12 +28,18 @@ class ExternalEditorLauncher: def build_diff_command( self, editor: TextEditorConfig, original_path: str, modified_path: str ) -> List[str]: + """ + [C: tests/test_external_editor.py:TestExternalEditorLauncher.test_build_diff_command, tests/test_external_editor_gui.py:test_verify_command_format, tests/test_external_editor_gui.py:test_verify_vscode_command_format] + """ cmd = [editor.path] + editor.diff_args + [original_path, modified_path] return cmd def launch_diff( self, editor_name: Optional[str], original_path: str, modified_path: str ) -> Optional[subprocess.Popen]: + """ + [C: src/gui_2.py:App._open_patch_in_external_editor, tests/test_external_editor.py:TestExternalEditorLauncher.test_launch_diff_file_not_found, tests/test_external_editor.py:TestExternalEditorLauncher.test_launch_diff_missing_editor, tests/test_external_editor.py:TestExternalEditorLauncher.test_launch_diff_success] + """ editor = self.get_editor(editor_name) if not editor: return None @@ -104,6 +116,9 @@ def auto_detect_vscode() -> Optional[TextEditorConfig]: def get_default_launcher() -> ExternalEditorLauncher: + """ + [C: src/gui_2.py:App._open_patch_in_external_editor, src/gui_2.py:App._render_external_editor_panel] + """ from src import models config = models.load_config() editors_config = config.get("tools", {}).get("text_editors", {}) @@ -137,6 +152,9 @@ def resolve_project_editor_override(project_path: Optional[str]) -> Optional[str def create_temp_modified_file(content: str) -> str: + """ + [C: src/gui_2.py:App._open_patch_in_external_editor, tests/test_external_editor.py:TestHelperFunctions.test_create_temp_modified_file] + """ with tempfile.NamedTemporaryFile(mode="w", suffix="_modified", delete=False, encoding="utf-8") as f: f.write(content) return f.name \ No newline at end of file diff --git a/src/file_cache.py b/src/file_cache.py index ad73d7f..2fbb091 100644 --- a/src/file_cache.py +++ b/src/file_cache.py @@ -46,11 +46,15 @@ _ast_cache: Dict[str, Tuple[float, tree_sitter.Tree]] = {} class ASTParser: """ - Parser for extracting AST-based views of source code. - Currently supports Python. - """ + + Parser for extracting AST-based views of source code. + Currently supports Python. + """ def __init__(self, language: str) -> None: + """ + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ if language not in ("python", "cpp", "c"): raise ValueError(f"Language '{language}' not supported yet.") self.language_name = language @@ -64,7 +68,10 @@ class ASTParser: self.parser = tree_sitter.Parser(self.language) def parse(self, code: str) -> tree_sitter.Tree: - """Parse the given code and return the tree-sitter Tree.""" + """ + Parse the given code and return the tree-sitter Tree. + [C: src/mcp_client.py:_search_file, src/mcp_client.py:derive_code_path, src/mcp_client.py:py_check_syntax, src/mcp_client.py:py_get_class_summary, src/mcp_client.py:py_get_definition, src/mcp_client.py:py_get_docstring, src/mcp_client.py:py_get_imports, src/mcp_client.py:py_get_signature, src/mcp_client.py:py_get_symbol_info, src/mcp_client.py:py_get_var_declaration, src/mcp_client.py:py_set_signature, src/mcp_client.py:py_set_var_declaration, src/mcp_client.py:py_update_definition, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/rag_engine.py:RAGEngine._chunk_code, src/summarize.py:_summarise_python, tests/test_ast_parser.py:test_ast_parser_parse, tests/test_tree_sitter_setup.py:test_tree_sitter_python_setup] + """ return self.parser.parse(bytes(code, "utf8")) def get_cached_tree(self, path: Optional[str], code: str) -> tree_sitter.Tree: @@ -129,8 +136,10 @@ class ASTParser: def get_skeleton(self, code: str, path: Optional[str] = None) -> str: """ - Returns a skeleton of a Python file (preserving docstrings, stripping function bodies). - """ + + Returns a skeleton of a Python file (preserving docstrings, stripping function bodies). + [C: src/mcp_client.py:py_get_skeleton, src/mcp_client.py:ts_c_get_skeleton, src/mcp_client.py:ts_cpp_get_skeleton, src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_ast_parser.py:test_ast_parser_get_skeleton_c, tests/test_ast_parser.py:test_ast_parser_get_skeleton_cpp, tests/test_ast_parser.py:test_ast_parser_get_skeleton_python, tests/test_context_pruner.py:test_ast_caching, tests/test_context_pruner.py:test_performance_large_file] + """ code_bytes = code.encode("utf8") tree = self.get_cached_tree(path, code) edits: List[Tuple[int, int, str]] = [] @@ -142,6 +151,9 @@ class ASTParser: return False def walk(node: tree_sitter.Node) -> None: + """ + [C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python] + """ if node.type == "function_definition": body = node.child_by_field_name("body") if body and body.type in ("block", "compound_statement"): @@ -178,10 +190,12 @@ class ASTParser: def get_curated_view(self, code: str, path: Optional[str] = None) -> str: """ - Returns a curated skeleton of a Python file. - Preserves function bodies if they have @core_logic decorator or # [HOT] comment. - Otherwise strips bodies but preserves docstrings. - """ + + Returns a curated skeleton of a Python file. + Preserves function bodies if they have @core_logic decorator or # [HOT] comment. + Otherwise strips bodies but preserves docstrings. + [C: src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_ast_parser.py:test_ast_parser_get_curated_view] + """ code_bytes = code.encode("utf8") tree = self.get_cached_tree(path, code) edits: List[Tuple[int, int, str]] = [] @@ -217,6 +231,9 @@ class ASTParser: return False def walk(node: tree_sitter.Node) -> None: + """ + [C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python] + """ if node.type == "function_definition": body = node.child_by_field_name("body") if body and body.type in ("block", "compound_statement"): @@ -250,9 +267,11 @@ class ASTParser: def get_targeted_view(self, code: str, function_names: List[str], path: Optional[str] = None) -> str: """ - Returns a targeted view of the code including only the specified functions - and their dependencies up to depth 2. - """ + + Returns a targeted view of the code including only the specified functions + and their dependencies up to depth 2. + [C: src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_ast_parser.py:test_ast_parser_get_targeted_view, tests/test_context_pruner.py:test_class_targeted_extraction, tests/test_context_pruner.py:test_targeted_extraction] + """ code_bytes = code.encode("utf8") tree = self.get_cached_tree(path, code) all_functions = {} @@ -412,8 +431,10 @@ class ASTParser: def get_definition(self, code: str, name: str, path: Optional[str] = None) -> str: """ - Returns the full source code for a specific definition by name. - Supports 'ClassName::method' or 'method' for C++. + + Returns the full source code for a specific definition by name. + Supports 'ClassName::method' or 'method' for C++. + [C: src/mcp_client.py:trace, src/mcp_client.py:ts_c_get_definition, src/mcp_client.py:ts_cpp_get_definition, tests/test_ast_parser.py:test_ast_parser_get_definition_c, tests/test_ast_parser.py:test_ast_parser_get_definition_cpp, tests/test_ast_parser.py:test_ast_parser_get_definition_cpp_template] """ code_bytes = code.encode("utf8") tree = self.get_cached_tree(path, code) @@ -421,6 +442,9 @@ class ASTParser: parts = re.split(r'::|\.', name) def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]: + """ + [C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python] + """ if not target_parts: return None target = target_parts[0] @@ -480,8 +504,10 @@ class ASTParser: def get_signature(self, code: str, name: str, path: Optional[str] = None) -> str: """ - Returns only the signature part of a function or method. - For C/C++, this is the code from the start of the definition until the block start '{'. + + Returns only the signature part of a function or method. + For C/C++, this is the code from the start of the definition until the block start '{'. + [C: src/mcp_client.py:ts_c_get_signature, src/mcp_client.py:ts_cpp_get_signature, tests/test_ast_parser.py:test_ast_parser_get_signature_c, tests/test_ast_parser.py:test_ast_parser_get_signature_cpp] """ code_bytes = code.encode("utf8") tree = self.get_cached_tree(path, code) @@ -489,6 +515,9 @@ class ASTParser: parts = re.split(r'::|\.', name) def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]: + """ + [C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python] + """ if not target_parts: return None target = target_parts[0] @@ -559,13 +588,18 @@ class ASTParser: def get_code_outline(self, code: str, path: Optional[str] = None) -> str: """ - Returns a hierarchical outline of the code (classes, structs, functions, methods). - """ + + Returns a hierarchical outline of the code (classes, structs, functions, methods). + [C: src/mcp_client.py:ts_c_get_code_outline, src/mcp_client.py:ts_cpp_get_code_outline, tests/test_ast_parser.py:test_ast_parser_get_code_outline_c, tests/test_ast_parser.py:test_ast_parser_get_code_outline_cpp] + """ code_bytes = code.encode("utf8") tree = self.get_cached_tree(path, code) output = [] def walk(node: tree_sitter.Node, indent: int = 0) -> None: + """ + [C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python] + """ ntype = node.type label = "" if ntype in ("class_definition", "class_specifier"): @@ -595,7 +629,9 @@ class ASTParser: def update_definition(self, code: str, name: str, new_content: str, path: Optional[str] = None) -> str: """ - Surgically replace the definition of a class or function by name. + + Surgically replace the definition of a class or function by name. + [C: src/mcp_client.py:ts_c_update_definition, src/mcp_client.py:ts_cpp_update_definition, tests/test_ast_parser.py:test_ast_parser_update_definition_cpp] """ code_bytes = code.encode("utf8") tree = self.get_cached_tree(path, code) @@ -603,6 +639,9 @@ class ASTParser: parts = re.split(r'::|\.', name) def walk(node: tree_sitter.Node, target_parts: List[str]) -> Optional[tree_sitter.Node]: + """ + [C: src/mcp_client.py:_search_file, src/mcp_client.py:py_find_usages, src/mcp_client.py:py_get_hierarchy, src/mcp_client.py:trace, src/outline_tool.py:CodeOutliner.outline, src/outline_tool.py:CodeOutliner.walk, src/summarize.py:_summarise_python] + """ if not target_parts: return None target = target_parts[0] @@ -677,4 +716,3 @@ def evict(path: Path) -> None: def list_cached() -> List[Dict[str, Any]]: return [] - diff --git a/src/gemini_cli_adapter.py b/src/gemini_cli_adapter.py index 5d90e68..f8ec5d5 100644 --- a/src/gemini_cli_adapter.py +++ b/src/gemini_cli_adapter.py @@ -44,10 +44,14 @@ from typing import Optional, Callable, Any class GeminiCliAdapter: """ - Adapter for the Gemini CLI that parses streaming JSON output. - """ + + Adapter for the Gemini CLI that parses streaming JSON output. + """ def __init__(self, binary_path: str = "gemini"): - """Initializes the adapter with the path to the gemini CLI executable.""" + """ + Initializes the adapter with the path to the gemini CLI executable. + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self.binary_path = binary_path self.session_id: Optional[str] = None self.last_usage: Optional[dict[str, Any]] = None @@ -56,9 +60,11 @@ class GeminiCliAdapter: def send(self, message: str, safety_settings: list[Any] | None = None, system_instruction: str | None = None, model: str | None = None, stream_callback: Optional[Callable[[str], None]] = None) -> dict[str, Any]: """ - Sends a message to the Gemini CLI and processes the streaming JSON output. - Uses non-blocking line-by-line reading to allow stream_callback. - """ + + Sends a message to the Gemini CLI and processes the streaming JSON output. + Uses non-blocking line-by-line reading to allow stream_callback. + [C: simulation/user_agent.py:UserSimAgent.generate_response, src/multi_agent_conductor.py:run_worker_lifecycle, src/native_orchestrator.py:NativeOrchestrator.execute_ticket, src/orchestrator_pm.py:generate_tracks, tests/test_ai_cache_tracking.py:test_gemini_cache_tracking, tests/test_ai_client_cli.py:test_ai_client_send_gemini_cli, tests/test_api_events.py:test_send_emits_events_proper, tests/test_api_events.py:test_send_emits_tool_events, tests/test_deepseek_provider.py:test_deepseek_completion_logic, tests/test_deepseek_provider.py:test_deepseek_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoner_payload_verification, tests/test_deepseek_provider.py:test_deepseek_reasoning_logic, tests/test_deepseek_provider.py:test_deepseek_streaming, tests/test_deepseek_provider.py:test_deepseek_tool_calling, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_full_flow_integration, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_captures_usage_metadata, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_handles_tool_use_events, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_parses_jsonl_output, tests/test_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_starts_subprocess_with_correct_args, tests/test_gemini_cli_adapter_parity.py:TestGeminiCliAdapterParity.test_send_parses_tool_calls_from_streaming_json, tests/test_gemini_cli_adapter_parity.py:TestGeminiCliAdapterParity.test_send_starts_subprocess_with_model, tests/test_gemini_cli_edge_cases.py:test_gemini_cli_context_bleed_prevention, tests/test_gemini_cli_edge_cases.py:test_gemini_cli_loop_termination, tests/test_gemini_cli_integration.py:test_gemini_cli_full_integration, tests/test_gemini_cli_integration.py:test_gemini_cli_rejection_and_history, tests/test_gemini_cli_parity_regression.py:test_get_history_bleed_stats, tests/test_gemini_cli_parity_regression.py:test_send_invokes_adapter_send, tests/test_gui2_mcp.py:test_mcp_tool_call_is_dispatched, tests/test_tier4_interceptor.py:test_ai_client_passes_qa_callback, tests/test_token_usage.py:test_token_usage_tracking, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast] + """ start_time = time.time() command_parts = [self.binary_path] if model: @@ -185,9 +191,10 @@ class GeminiCliAdapter: def count_tokens(self, contents: list[str]) -> int: """ - Provides a character-based token estimation for the Gemini CLI. - Uses 4 chars/token as a conservative average. - """ + + Provides a character-based token estimation for the Gemini CLI. + Uses 4 chars/token as a conservative average. + [C: tests/test_gemini_cli_adapter_parity.py:TestGeminiCliAdapterParity.test_count_tokens_fallback] + """ total_chars = len("\n".join(contents)) return total_chars // 4 - diff --git a/src/gui_2.py b/src/gui_2.py index be6746d..3ddabdc 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -103,6 +103,9 @@ class App: def __init__(self) -> None: # Initialize controller and delegate state + """ + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self.controller = app_controller.AppController() self.controller._app = self from src import history @@ -429,7 +432,10 @@ class App: self._apply_snapshot(entry.state) def shutdown(self) -> None: - """Cleanly shuts down the app's background tasks and saves state.""" + """ + Cleanly shuts down the app's background tasks and saves state. + [C: tests/conftest.py:app_instance, tests/conftest.py:mock_app] + """ try: if hasattr(self, 'runner_params') and self.runner_params.ini_filename: imgui.save_ini_settings_to_disk(self.runner_params.ini_filename) @@ -438,6 +444,9 @@ class App: self.controller.shutdown() def save_context_preset(self, name: str) -> None: + """ + [C: tests/test_context_presets.py:test_save_context_preset] + """ sys.stderr.write(f"[DEBUG] save_context_preset called with: {name}\n") sys.stderr.flush() if 'context_presets' not in self.controller.project: @@ -451,6 +460,9 @@ class App: sys.stderr.flush() def load_context_preset(self, name: str) -> None: + """ + [C: tests/test_context_presets.py:test_load_context_preset, tests/test_context_presets.py:test_load_nonexistent_preset] + """ presets = self.controller.project.get('context_presets', {}) if name in presets: preset = presets[name] @@ -458,6 +470,9 @@ class App: self.screenshots = list(preset.get('screenshots', [])) def delete_context_preset(self, name: str) -> None: + """ + [C: tests/test_context_presets.py:test_delete_context_preset, tests/test_context_presets.py:test_delete_nonexistent_preset_no_error] + """ if 'context_presets' in self.controller.project: self.controller.project['context_presets'].pop(name, None) self.controller._save_active_project() @@ -597,6 +612,9 @@ class App: imgui.pop_id() def _show_menus(self) -> None: + """ + [C: tests/test_gui_window_controls.py:test_gui_window_controls_minimize_maximize_close] + """ if imgui.begin_menu("manual slop"): if imgui.menu_item("Quit", "Ctrl+Q", False)[0]: self.runner_params.app_shall_exit = True @@ -719,6 +737,9 @@ class App: pass def _render_shader_live_editor(self) -> None: + """ + [C: tests/test_shader_live_editor.py:test_shader_live_editor_renders] + """ if self.show_windows.get('Shader Editor', False): exp, opened = imgui.begin('Shader Editor', self.show_windows['Shader Editor']) self.show_windows['Shader Editor'] = bool(opened) @@ -2044,6 +2065,9 @@ class App: if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_projects_panel") def _save_paths(self): + """ + [C: tests/test_gui_paths.py:test_save_paths] + """ self.config["paths"] = { "logs_dir": self.ui_logs_dir, "scripts_dir": self.ui_scripts_dir @@ -2295,6 +2319,9 @@ class App: self._patch_error_message = str(e) def _render_log_management(self) -> None: + """ + [C: tests/test_log_management_ui.py:test_render_log_management_logic] + """ if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_log_management") exp, opened = imgui.begin("Log Management", self.show_windows["Log Management"]) self.show_windows["Log Management"] = bool(opened) @@ -2797,6 +2824,9 @@ def hello(): if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_screenshots_panel") def _render_discussion_panel(self) -> None: + """ + [C: tests/test_discussion_takes_gui.py:test_render_discussion_tabs, tests/test_discussion_takes_gui.py:test_switching_discussion_via_tabs, tests/test_gui_discussion_tabs.py:test_discussion_tabs_rendered, tests/test_gui_phase4.py:test_track_discussion_toggle, tests/test_gui_symbol_navigation.py:test_render_discussion_panel_symbol_lookup] + """ if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_discussion_panel") # THINKING indicator is_thinking = self.ai_status in ['sending...', 'streaming...', 'running powershell...'] @@ -3175,7 +3205,10 @@ def hello(): if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_discussion_panel") def _render_synthesis_panel(self) -> None: - """Renders a panel for synthesizing multiple discussion takes.""" + """ + Renders a panel for synthesizing multiple discussion takes. + [C: tests/test_gui_synthesis.py:test_render_synthesis_panel] + """ imgui.text("Select takes to synthesize:") discussions = self.project.get('discussion', {}).get('discussions', {}) if not hasattr(self, 'ui_synthesis_selected_takes'): @@ -3917,18 +3950,27 @@ def hello(): if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_tool_calls_panel") def bulk_execute(self) -> None: + """ + [C: tests/test_ticket_queue.py:TestBulkOperations.test_bulk_execute] + """ for tid in self.ui_selected_tickets: t = next((t for t in self.active_tickets if str(t.get('id', '')) == tid), None) if t: t['status'] = 'in_progress' self._push_mma_state_update() def bulk_skip(self) -> None: + """ + [C: tests/test_ticket_queue.py:TestBulkOperations.test_bulk_skip] + """ for tid in self.ui_selected_tickets: t = next((t for t in self.active_tickets if str(t.get('id', '')) == tid), None) if t: t['status'] = 'completed' self._push_mma_state_update() def bulk_block(self) -> None: + """ + [C: tests/test_ticket_queue.py:TestBulkOperations.test_bulk_block] + """ for tid in self.ui_selected_tickets: t = next((t for t in self.active_tickets if str(t.get('id', '')) == tid), None) if t: t['status'] = 'blocked' @@ -3980,6 +4022,9 @@ def hello(): self._push_mma_state_update() def _reorder_ticket(self, src_idx: int, dst_idx: int) -> None: + """ + [C: tests/test_ticket_queue.py:TestReorder.test_reorder_ticket_invalid, tests/test_ticket_queue.py:TestReorder.test_reorder_ticket_valid] + """ if src_idx == dst_idx: return new_tickets = list(self.active_tickets) ticket = new_tickets.pop(src_idx) @@ -3999,6 +4044,9 @@ def hello(): self._push_mma_state_update() def _render_ticket_queue(self) -> None: + """ + [C: tests/test_gui_kill_button.py:test_render_ticket_queue_table_columns] + """ imgui.text("Ticket Queue Management") if not self.active_track: imgui.text_disabled("No active track.") @@ -4126,6 +4174,9 @@ def hello(): imgui.end_table() def _render_mma_dashboard(self) -> None: + """ + [C: tests/test_gui_progress.py:test_render_mma_dashboard_progress, tests/test_mma_approval_indicators.py:TestMMAApprovalIndicators.test_approval_badge_shown_when_ask_dialog_pending, tests/test_mma_approval_indicators.py:TestMMAApprovalIndicators.test_approval_badge_shown_when_mma_approval_pending, tests/test_mma_approval_indicators.py:TestMMAApprovalIndicators.test_approval_badge_shown_when_spawn_pending, tests/test_mma_approval_indicators.py:TestMMAApprovalIndicators.test_no_approval_badge_when_idle] + """ if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_mma_dashboard") # Focus Agent dropdown @@ -4734,6 +4785,9 @@ def hello(): imgui.text_colored(imgui.ImVec4(1, 0, 0, 1), f"Error loading beads: {e}") def _render_tier_stream_panel(self, tier_key: str, stream_key: str | None) -> None: + """ + [C: tests/test_mma_dashboard_streams.py:TestMMADashboardStreams.test_tier1_renders_stream_content, tests/test_mma_dashboard_streams.py:TestMMADashboardStreams.test_tier3_renders_worker_subheaders] + """ if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_tier_stream_panel") if self.is_viewing_prior_session: imgui.text_colored(vec4(255, 200, 100), "HISTORICAL VIEW - READ ONLY") @@ -5135,7 +5189,10 @@ def hello(): theme.apply_current() def run(self) -> None: - """Initializes the ImGui runner and starts the main application loop.""" + """ + Initializes the ImGui runner and starts the main application loop. + [C: simulation/sim_base.py:run_sim, src/mcp_client.py:get_git_diff, src/project_manager.py:get_git_commit, src/project_manager.py:get_git_log, src/rag_engine.py:RAGEngine._search_mcp, src/shell_runner.py:run_powershell, tests/conftest.py:kill_process_tree, tests/conftest.py:live_gui, tests/test_conductor_abort_event.py:test_conductor_abort_event_populated, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:get_vscode_processes, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_custom_window.py:test_app_window_is_borderless, tests/test_headless_simulation.py:module, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_mock_gemini_cli.py:run_mock, tests/test_orchestration_logic.py:test_conductor_engine_run, tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:test_execution_simulation_run, tests/test_sim_tools.py:test_tools_simulation_run] + """ if "--headless" in sys.argv: print("Headless mode active") self._fetch_models(self.current_provider) @@ -5202,4 +5259,3 @@ def main() -> None: if __name__ == "__main__": main() - diff --git a/src/history.py b/src/history.py index 5da2041..a033e34 100644 --- a/src/history.py +++ b/src/history.py @@ -19,6 +19,9 @@ class UISnapshot: screenshots: list[str] def to_dict(self) -> dict: + """ + [C: src/models.py:ExternalEditorConfig.to_dict, src/models.py:MCPConfiguration.to_dict, src/models.py:RAGConfig.to_dict, src/models.py:ToolPreset.to_dict, src/models.py:Track.to_dict, src/models.py:TrackState.to_dict, src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return { "ai_input": self.ai_input, "project_system_prompt": self.project_system_prompt, @@ -36,6 +39,9 @@ class UISnapshot: @classmethod def from_dict(cls, data: dict) -> "UISnapshot": + """ + [C: src/models.py:ExternalEditorConfig.from_dict, src/models.py:MCPConfiguration.from_dict, src/models.py:RAGConfig.from_dict, src/models.py:ToolPreset.from_dict, src/models.py:Track.from_dict, src/models.py:TrackState.from_dict, src/models.py:load_mcp_config, src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return cls( ai_input=data.get("ai_input", ""), project_system_prompt=data.get("project_system_prompt", ""), @@ -59,14 +65,19 @@ class HistoryEntry: class HistoryManager: def __init__(self, max_capacity: int = 100): + """ + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self.max_capacity = max_capacity self._undo_stack: typing.List[HistoryEntry] = [] self._redo_stack: typing.List[HistoryEntry] = [] def push(self, state: typing.Any, description: str) -> None: """ - Pushes a new state to the undo stack and clears the redo stack. - If the undo stack exceeds max_capacity, the oldest state is removed. + + Pushes a new state to the undo stack and clears the redo stack. + If the undo stack exceeds max_capacity, the oldest state is removed. + [C: tests/test_history.py:test_jump_to_undo, tests/test_history.py:test_max_capacity, tests/test_history.py:test_push_state, tests/test_history.py:test_redo_cleared_on_push, tests/test_history.py:test_undo_redo] """ entry = HistoryEntry(state=state, description=description) self._undo_stack.append(entry) @@ -76,8 +87,10 @@ class HistoryManager: def undo(self, current_state: typing.Any, current_description: str = "Current State") -> typing.Optional[HistoryEntry]: """ - Undoes the last action by moving the current_state to the redo stack - and returning the top of the undo stack. + + Undoes the last action by moving the current_state to the redo stack + and returning the top of the undo stack. + [C: tests/test_history.py:test_redo_cleared_on_push, tests/test_history.py:test_undo_redo] """ if not self._undo_stack: return None @@ -88,8 +101,10 @@ class HistoryManager: def redo(self, current_state: typing.Any, current_description: str = "Current State") -> typing.Optional[HistoryEntry]: """ - Redoes the last undone action by moving the current_state to the undo stack - and returning the top of the redo stack. + + Redoes the last undone action by moving the current_state to the undo stack + and returning the top of the redo stack. + [C: tests/test_history.py:test_undo_redo] """ if not self._redo_stack: return None @@ -107,7 +122,10 @@ class HistoryManager: return len(self._redo_stack) > 0 def get_history(self) -> typing.List[typing.Dict[str, typing.Any]]: - """Returns a list of descriptions and timestamps for the undo stack.""" + """ + Returns a list of descriptions and timestamps for the undo stack. + [C: tests/test_history.py:test_initial_state, tests/test_history.py:test_push_state] + """ return [ {"description": e.description, "timestamp": e.timestamp} for e in self._undo_stack @@ -115,8 +133,10 @@ class HistoryManager: def jump_to_undo(self, index: int, current_state: typing.Any, current_description: str = "Before Jump") -> typing.Optional[HistoryEntry]: """ - Jumps to a specific state in the undo stack by moving subsequent states - and the current_state to the redo stack. + + Jumps to a specific state in the undo stack by moving subsequent states + and the current_state to the redo stack. + [C: tests/test_history.py:test_jump_to_undo] """ if index < 0 or index >= len(self._undo_stack): return None @@ -128,4 +148,4 @@ class HistoryManager: while len(self._undo_stack) > index + 1: self._redo_stack.append(self._undo_stack.pop()) - return self._undo_stack.pop() + return self._undo_stack.pop() \ No newline at end of file diff --git a/src/log_pruner.py b/src/log_pruner.py index 0f2a528..8be1a1f 100644 --- a/src/log_pruner.py +++ b/src/log_pruner.py @@ -7,31 +7,36 @@ from src.log_registry import LogRegistry class LogPruner: """ - Handles the automated deletion of old and insignificant session logs. - Ensures that only whitelisted or significant sessions (based on size/content) - are preserved long-term. - """ + + Handles the automated deletion of old and insignificant session logs. + Ensures that only whitelisted or significant sessions (based on size/content) + are preserved long-term. + """ def __init__(self, log_registry: LogRegistry, logs_dir: str) -> None: """ - Initializes the LogPruner. - - Args: - log_registry: An instance of LogRegistry to check session data. - logs_dir: The path to the directory containing session sub-directories. - """ + + Initializes the LogPruner. + + Args: + log_registry: An instance of LogRegistry to check session data. + logs_dir: The path to the directory containing session sub-directories. + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self.log_registry = log_registry self.logs_dir = logs_dir def prune(self, max_age_days: int = 1, min_size_kb: int = 2) -> None: """ - Prunes old and small session directories from the logs directory. - - Deletes session directories that meet the following criteria: - 1. The session start time is older than max_age_days. - 2. The session name is NOT in the whitelist provided by the LogRegistry. - 3. The total size of all files within the session directory is less than min_size_kb. - """ + + Prunes old and small session directories from the logs directory. + + Deletes session directories that meet the following criteria: + 1. The session start time is older than max_age_days. + 2. The session name is NOT in the whitelist provided by the LogRegistry. + 3. The total size of all files within the session directory is less than min_size_kb. + [C: tests/test_log_pruner.py:test_prune_old_insignificant_logs, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_handles_relative_paths_starting_with_logs, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_removes_empty_sessions_regardless_of_age, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_removes_sessions_without_metadata_regardless_of_age, tests/test_logging_e2e.py:test_logging_e2e] + """ now = datetime.now() cutoff_time = now - timedelta(days=max_age_days) # Ensure the base logs directory exists. @@ -115,4 +120,3 @@ class LogPruner: sys.stderr.write(f"[LogPruner] Error removing {resolved_path}: {e}\n") self.log_registry.save_registry() - diff --git a/src/log_registry.py b/src/log_registry.py index 65cbcda..81ce8c4 100644 --- a/src/log_registry.py +++ b/src/log_registry.py @@ -47,17 +47,20 @@ from typing import Any class LogRegistry: """ - Manages a persistent registry of session logs using a TOML file. - Tracks session paths, start times, whitelisting status, and metadata. - """ + + Manages a persistent registry of session logs using a TOML file. + Tracks session paths, start times, whitelisting status, and metadata. + """ def __init__(self, registry_path: str) -> None: """ - Initializes the LogRegistry with a path to the registry file. - - Args: - registry_path (str): The file path to the TOML registry. - """ + + Initializes the LogRegistry with a path to the registry file. + + Args: + registry_path (str): The file path to the TOML registry. + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self.registry_path = registry_path self.data: dict[str, dict[str, Any]] = {} self.load_registry() @@ -69,9 +72,10 @@ class LogRegistry: def load_registry(self) -> None: """ - Loads the registry data from the TOML file into memory. - Handles date/time conversions from TOML-native formats to strings for consistency. - """ + + Loads the registry data from the TOML file into memory. + Handles date/time conversions from TOML-native formats to strings for consistency. + """ if os.path.exists(self.registry_path): try: with open(self.registry_path, 'rb') as f: @@ -97,9 +101,11 @@ class LogRegistry: def save_registry(self) -> None: """ - Serializes and saves the current registry data to the TOML file. - Converts internal datetime objects to ISO format strings for compatibility. - """ + + Serializes and saves the current registry data to the TOML file. + Converts internal datetime objects to ISO format strings for compatibility. + [C: tests/test_logging_e2e.py:test_logging_e2e] + """ try: # Convert datetime objects to ISO format strings for TOML serialization data_to_save: dict[str, Any] = {} @@ -130,13 +136,15 @@ class LogRegistry: def register_session(self, session_id: str, path: str, start_time: datetime | str) -> None: """ - Registers a new session in the registry. - - Args: - session_id (str): Unique identifier for the session. - path (str): File path to the session's log directory. - start_time (datetime|str): The timestamp when the session started. - """ + + Registers a new session in the registry. + + Args: + session_id (str): Unique identifier for the session. + path (str): File path to the session's log directory. + start_time (datetime|str): The timestamp when the session started. + [C: src/session_logger.py:open_session, tests/test_auto_whitelist.py:test_auto_whitelist_keywords, tests/test_auto_whitelist.py:test_auto_whitelist_large_size, tests/test_auto_whitelist.py:test_auto_whitelist_message_count, tests/test_auto_whitelist.py:test_no_auto_whitelist_insignificant, tests/test_log_pruner.py:test_prune_old_insignificant_logs, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_get_old_non_whitelisted_sessions_includes_empty_sessions, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_get_old_non_whitelisted_sessions_includes_sessions_without_metadata, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_handles_relative_paths_starting_with_logs, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_removes_empty_sessions_regardless_of_age, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_removes_sessions_without_metadata_regardless_of_age, tests/test_log_registry.py:TestLogRegistry.test_get_old_non_whitelisted_sessions, tests/test_log_registry.py:TestLogRegistry.test_is_session_whitelisted, tests/test_log_registry.py:TestLogRegistry.test_register_session, tests/test_log_registry.py:TestLogRegistry.test_update_session_metadata, tests/test_logging_e2e.py:test_logging_e2e] + """ if session_id in self.data: print(f"Warning: Session ID '{session_id}' already exists. Overwriting.") # Store start_time internally as a string to satisfy tests @@ -154,16 +162,18 @@ class LogRegistry: def update_session_metadata(self, session_id: str, message_count: int, errors: int, size_kb: int, whitelisted: bool, reason: str) -> None: """ - Updates metadata fields for an existing session. - - Args: - session_id (str): Unique identifier for the session. - message_count (int): Total number of messages in the session. - errors (int): Number of errors identified in logs. - size_kb (int): Total size of the session logs in kilobytes. - whitelisted (bool): Whether the session should be protected from pruning. - reason (str): Explanation for the current whitelisting status. - """ + + Updates metadata fields for an existing session. + + Args: + session_id (str): Unique identifier for the session. + message_count (int): Total number of messages in the session. + errors (int): Number of errors identified in logs. + size_kb (int): Total size of the session logs in kilobytes. + whitelisted (bool): Whether the session should be protected from pruning. + reason (str): Explanation for the current whitelisting status. + [C: tests/test_auto_whitelist.py:test_auto_whitelist_large_size, tests/test_auto_whitelist.py:test_auto_whitelist_message_count, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_get_old_non_whitelisted_sessions_includes_empty_sessions, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_prune_removes_empty_sessions_regardless_of_age, tests/test_log_registry.py:TestLogRegistry.test_get_old_non_whitelisted_sessions, tests/test_log_registry.py:TestLogRegistry.test_is_session_whitelisted, tests/test_log_registry.py:TestLogRegistry.test_update_session_metadata] + """ if session_id not in self.data: print(f"Error: Session ID '{session_id}' not found for metadata update.") return @@ -186,14 +196,16 @@ class LogRegistry: def is_session_whitelisted(self, session_id: str) -> bool: """ - Checks if a specific session is marked as whitelisted. - - Args: - session_id (str): Unique identifier for the session. - - Returns: - bool: True if whitelisted, False otherwise. - """ + + Checks if a specific session is marked as whitelisted. + + Args: + session_id (str): Unique identifier for the session. + + Returns: + bool: True if whitelisted, False otherwise. + [C: tests/test_auto_whitelist.py:test_auto_whitelist_keywords, tests/test_auto_whitelist.py:test_auto_whitelist_large_size, tests/test_auto_whitelist.py:test_auto_whitelist_message_count, tests/test_auto_whitelist.py:test_no_auto_whitelist_insignificant, tests/test_log_registry.py:TestLogRegistry.test_is_session_whitelisted, tests/test_logging_e2e.py:test_logging_e2e] + """ session_data = self.data.get(session_id) if session_data is None: return False # Non-existent sessions are not whitelisted @@ -202,13 +214,15 @@ class LogRegistry: def update_auto_whitelist_status(self, session_id: str) -> None: """ - Analyzes session logs and updates whitelisting status based on heuristics. - Sessions are automatically whitelisted if they contain error keywords, - have a high message count, or exceed a size threshold. - - Args: - session_id (str): Unique identifier for the session to analyze. - """ + + Analyzes session logs and updates whitelisting status based on heuristics. + Sessions are automatically whitelisted if they contain error keywords, + have a high message count, or exceed a size threshold. + + Args: + session_id (str): Unique identifier for the session to analyze. + [C: src/session_logger.py:close_session] + """ if session_id not in self.data: return session_data = self.data[session_id] @@ -260,16 +274,18 @@ class LogRegistry: def get_old_non_whitelisted_sessions(self, cutoff_datetime: datetime) -> list[dict[str, Any]]: """ - Retrieves a list of sessions that are older than a specific cutoff time - and are not marked as whitelisted. - Also includes non-whitelisted sessions that are empty (message_count=0 or size_kb=0). - - Args: - cutoff_datetime (datetime): The threshold time for identifying old sessions. - - Returns: - list: A list of dictionaries containing session details (id, path, start_time). - """ + + Retrieves a list of sessions that are older than a specific cutoff time + and are not marked as whitelisted. + Also includes non-whitelisted sessions that are empty (message_count=0 or size_kb=0). + + Args: + cutoff_datetime (datetime): The threshold time for identifying old sessions. + + Returns: + list: A list of dictionaries containing session details (id, path, start_time). + [C: tests/test_log_pruner.py:test_prune_old_insignificant_logs, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_get_old_non_whitelisted_sessions_includes_empty_sessions, tests/test_log_pruning_heuristic.py:TestLogPruningHeuristic.test_get_old_non_whitelisted_sessions_includes_sessions_without_metadata, tests/test_log_registry.py:TestLogRegistry.test_get_old_non_whitelisted_sessions] + """ old_sessions = [] for session_id, session_data in self.data.items(): # Check if session is older than cutoff and not whitelisted @@ -301,4 +317,3 @@ class LogRegistry: }) return old_sessions - diff --git a/src/markdown_helper.py b/src/markdown_helper.py index a4cd0bf..e936b0d 100644 --- a/src/markdown_helper.py +++ b/src/markdown_helper.py @@ -9,10 +9,14 @@ from typing import Optional, Dict, Callable class MarkdownRenderer: """ - Hybrid Markdown renderer that uses imgui_md for text/headers - and ImGuiColorTextEdit for syntax-highlighted code blocks. - """ + + Hybrid Markdown renderer that uses imgui_md for text/headers + and ImGuiColorTextEdit for syntax-highlighted code blocks. + """ def __init__(self): + """ + [C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__] + """ self.options = imgui_md.MarkdownOptions() # Base path for fonts (Inter family) self.options.font_options.font_base_path = "fonts/Inter" @@ -62,7 +66,10 @@ class MarkdownRenderer: print(f"Error opening link {url}: {e}") def render(self, text: str, context_id: str = "default") -> None: - """Render Markdown text with code block interception.""" + """ + Render Markdown text with code block interception. + [C: tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render] + """ if not text: return @@ -159,6 +166,9 @@ def get_renderer() -> MarkdownRenderer: return _renderer def render(text: str, context_id: str = "default") -> None: + """ + [C: tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render] + """ get_renderer().render(text, context_id) def render_unindented(text: str) -> None: @@ -166,4 +176,3 @@ def render_unindented(text: str) -> None: def render_code(code: str, lang: str = "", context_id: str = "default", block_idx: int = 0) -> None: get_renderer().render_code(code, lang, context_id, block_idx) - diff --git a/src/mcp_client.py b/src/mcp_client.py index b824fa0..1bad57e 100644 --- a/src/mcp_client.py +++ b/src/mcp_client.py @@ -96,12 +96,14 @@ perf_monitor_callback: Optional[Callable[[], dict[str, Any]]] = None def configure(file_items: list[dict[str, Any]], extra_base_dirs: list[str] | None = None) -> None: """ - Build the allowlist from aggregate file_items. - Called by ai_client before each send so the list reflects the current project. - - file_items : list of dicts from aggregate.build_file_items() - extra_base_dirs : additional directory roots to allow traversal of - """ + + Build the allowlist from aggregate file_items. + Called by ai_client before each send so the list reflects the current project. + + file_items : list of dicts from aggregate.build_file_items() + extra_base_dirs : additional directory roots to allow traversal of + [C: tests/conftest.py:reset_ai_client, tests/test_arch_boundary_phase1.py:TestArchBoundaryPhase1.test_mcp_client_whitelist_enforcement, tests/test_mcp_client_beads.py:test_bd_mcp_tools] + """ global _allowed_paths, _base_dirs, _primary_base_dir _allowed_paths = set() _base_dirs = set() @@ -123,15 +125,17 @@ def configure(file_items: list[dict[str, Any]], extra_base_dirs: list[str] | Non def _is_allowed(path: Path) -> bool: """ - Return True if `path` is within the allowlist. - A path is allowed if: - - it is explicitly in _allowed_paths, OR - - it is contained within (or equal to) one of the _base_dirs - All paths are resolved (follows symlinks) before comparison to prevent - symlink-based path traversal. - - CRITICAL: Blacklisted files (history) are NEVER allowed. - """ + + Return True if `path` is within the allowlist. + A path is allowed if: + - it is explicitly in _allowed_paths, OR + - it is contained within (or equal to) one of the _base_dirs + All paths are resolved (follows symlinks) before comparison to prevent + symlink-based path traversal. + + CRITICAL: Blacklisted files (history) are NEVER allowed. + [C: tests/test_arch_boundary_phase1.py:TestArchBoundaryPhase1.test_mcp_client_whitelist_enforcement, tests/test_history_management.py:test_mcp_blacklist] + """ from src.paths import get_config_path from src.ai_client import get_credentials_path @@ -170,9 +174,10 @@ def _is_allowed(path: Path) -> bool: def _resolve_and_check(raw_path: str) -> tuple[Path | None, str]: """ - Resolve raw_path and verify it passes the allowlist check. - Returns (resolved_path, error_string). error_string is empty on success. - """ + + Resolve raw_path and verify it passes the allowlist check. + Returns (resolved_path, error_string). error_string is empty on success. + """ try: p = Path(raw_path) if not p.is_absolute() and _primary_base_dir: @@ -232,9 +237,10 @@ def list_directory(path: str) -> str: def search_files(path: str, pattern: str) -> str: """ - Search for files matching a glob pattern within path. - pattern examples: '*.py', '**/*.toml', 'src/**/*.rs' - """ + + Search for files matching a glob pattern within path. + pattern examples: '*.py', '**/*.toml', 'src/**/*.rs' + """ p, err = _resolve_and_check(path) if err or p is None: return err @@ -262,10 +268,11 @@ def search_files(path: str, pattern: str) -> str: def get_file_summary(path: str) -> str: """ - Return the heuristic summary for a file (same as the initial context block). - For .py files: imports, classes, methods, functions, constants. - For .toml: table keys. For .md: headings. Others: line count + preview. - """ + + Return the heuristic summary for a file (same as the initial context block). + For .py files: imports, classes, methods, functions, constants. + For .toml: table keys. For .md: headings. Others: line count + preview. + """ p, err = _resolve_and_check(path) if err or p is None: return err @@ -281,8 +288,9 @@ def get_file_summary(path: str) -> str: def py_get_skeleton(path: str) -> str: """ - Returns a skeleton of a Python file (preserving docstrings, stripping function bodies). - """ + + Returns a skeleton of a Python file (preserving docstrings, stripping function bodies). + """ p, err = _resolve_and_check(path) if err: return err @@ -300,7 +308,10 @@ def py_get_skeleton(path: str) -> str: return f"ERROR generating skeleton for '{path}': {e}" def ts_c_get_skeleton(path: str) -> str: - """Returns a skeleton of a C file.""" + """ + Returns a skeleton of a C file. + [C: tests/test_ts_c_tools.py:test_ts_c_get_skeleton] + """ p, err = _resolve_and_check(path) if err: return err assert p is not None @@ -314,7 +325,10 @@ def ts_c_get_skeleton(path: str) -> str: return f"ERROR generating skeleton for '{path}': {e}" def ts_cpp_get_skeleton(path: str) -> str: - """Returns a skeleton of a C++ file.""" + """ + Returns a skeleton of a C++ file. + [C: tests/test_ts_cpp_tools.py:test_exhaustive_cpp_samples, tests/test_ts_cpp_tools.py:test_exhaustive_gencpp_samples, tests/test_ts_cpp_tools.py:test_ts_cpp_get_skeleton] + """ p, err = _resolve_and_check(path) if err: return err assert p is not None @@ -329,8 +343,9 @@ def ts_cpp_get_skeleton(path: str) -> str: def py_get_code_outline(path: str) -> str: """ - Returns a hierarchical outline of a code file (classes, functions, methods with line ranges). - """ + + Returns a hierarchical outline of a code file (classes, functions, methods with line ranges). + """ p, err = _resolve_and_check(path) if err: return err @@ -346,7 +361,10 @@ def py_get_code_outline(path: str) -> str: return f"ERROR generating outline for '{path}': {e}" def ts_c_get_code_outline(path: str) -> str: - """Returns a hierarchical outline of a C file.""" + """ + Returns a hierarchical outline of a C file. + [C: tests/test_ts_c_tools.py:test_ts_c_get_code_outline] + """ p, err = _resolve_and_check(path) if err: return err assert p is not None @@ -360,7 +378,10 @@ def ts_c_get_code_outline(path: str) -> str: return f"ERROR generating outline for '{path}': {e}" def ts_cpp_get_code_outline(path: str) -> str: - """Returns a hierarchical outline of a C++ file.""" + """ + Returns a hierarchical outline of a C++ file. + [C: tests/test_ts_cpp_tools.py:test_exhaustive_cpp_samples, tests/test_ts_cpp_tools.py:test_exhaustive_gencpp_samples, tests/test_ts_cpp_tools.py:test_ts_cpp_get_code_outline] + """ p, err = _resolve_and_check(path) if err: return err assert p is not None @@ -388,7 +409,10 @@ def ts_c_get_definition(path: str, name: str) -> str: return f"ERROR retrieving definition '{name}' from '{path}': {e}" def ts_cpp_get_definition(path: str, name: str) -> str: - """Returns the source code for a specific definition in a C++ file.""" + """ + Returns the source code for a specific definition in a C++ file. + [C: tests/test_ts_cpp_tools.py:test_exhaustive_cpp_samples, tests/test_ts_cpp_tools.py:test_exhaustive_gencpp_samples, tests/test_ts_cpp_tools.py:test_ts_cpp_update_definition, tests/test_ts_cpp_tools.py:test_ts_cpp_update_definition_gencpp] + """ p, err = _resolve_and_check(path) if err: return err assert p is not None @@ -448,7 +472,10 @@ def ts_c_update_definition(path: str, name: str, new_content: str) -> str: return f"ERROR updating definition '{name}' in '{path}': {e}" def ts_cpp_update_definition(path: str, name: str, new_content: str) -> str: - """Surgically replace the definition of a class or function in a C++ file.""" + """ + Surgically replace the definition of a class or function in a C++ file. + [C: tests/test_ts_cpp_tools.py:test_ts_cpp_update_definition, tests/test_ts_cpp_tools.py:test_ts_cpp_update_definition_gencpp] + """ p, err = _resolve_and_check(path) if err: return err assert p is not None @@ -504,8 +531,9 @@ def set_file_slice(path: str, start_line: int, end_line: int, new_content: str) def edit_file(path: str, old_string: str, new_string: str, replace_all: bool = False) -> str: """ - Replace exact string match in a file. Preserves indentation and line endings. - Drop-in replacement for native edit tool that destroys 1-space indentation. + + Replace exact string match in a file. Preserves indentation and line endings. + Drop-in replacement for native edit tool that destroys 1-space indentation. """ p, err = _resolve_and_check(path) if err: @@ -561,9 +589,10 @@ def _get_symbol_node(tree: ast.AST, name: str) -> Optional[ast.AST]: def py_get_symbol_info(path: str, name: str) -> tuple[str, int] | str: """ - Returns (source_code, line_number) for a specific class, function, or method definition. - If not found, returns an error string. - """ + + Returns (source_code, line_number) for a specific class, function, or method definition. + If not found, returns an error string. + """ p, err = _resolve_and_check(path) if err: return err @@ -587,10 +616,11 @@ def py_get_symbol_info(path: str, name: str) -> tuple[str, int] | str: def py_get_definition(path: str, name: str) -> str: """ - Returns the source code for a specific class, function, or method definition. - path: Path to the code file. - name: Name of the definition to retrieve (e.g., 'MyClass', 'my_function', 'MyClass.my_method'). - """ + + Returns the source code for a specific class, function, or method definition. + path: Path to the code file. + name: Name of the definition to retrieve (e.g., 'MyClass', 'my_function', 'MyClass.my_method'). + """ p, err = _resolve_and_check(path) if err: return err @@ -761,10 +791,11 @@ def py_set_var_declaration(path: str, name: str, new_declaration: str) -> str: def get_git_diff(path: str, base_rev: str = "HEAD", head_rev: str = "") -> str: """ - Returns the git diff for a file or directory. - base_rev: The base revision (default: HEAD) - head_rev: The head revision (optional) - """ + + Returns the git diff for a file or directory. + base_rev: The base revision (default: HEAD) + head_rev: The head revision (optional) + """ p, err = _resolve_and_check(path) if err: return err @@ -1114,7 +1145,10 @@ def fetch_url(url: str) -> str: return f"ERROR fetching URL '{url}': {e}" def get_ui_performance() -> str: - """Returns current UI performance metrics (FPS, Frame Time, CPU, Input Lag).""" + """ + Returns current UI performance metrics (FPS, Frame Time, CPU, Input Lag). + [C: tests/test_mcp_perf_tool.py:test_mcp_perf_tool_retrieval] + """ if perf_monitor_callback is None: return "INFO: UI Performance monitor is not available (headless/CLI mode). This tool is only functional when the Manual Slop GUI is running." try: @@ -1143,6 +1177,9 @@ class StdioMCPServer: return self._id_counter async def start(self): + """ + [C: src/multi_agent_conductor.py:WorkerPool.spawn, src/performance_monitor.py:PerformanceMonitor.__init__, tests/test_ai_client_concurrency.py:test_ai_client_tier_isolation, tests/test_conductor_engine_abort.py:test_kill_worker_sets_abort_and_joins_thread, tests/test_conductor_engine_v2.py:side_effect, tests/test_spawn_interception_v2.py:test_confirm_spawn_pushed_to_queue, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast] + """ self.status = 'starting' self.proc = await asyncio.create_subprocess_exec( self.config.command, @@ -1156,6 +1193,9 @@ class StdioMCPServer: self.status = 'running' async def stop(self): + """ + [C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast] + """ if self.proc: try: if self.proc.stdin: @@ -1216,7 +1256,10 @@ class ExternalMCPManager: self.servers = {} async def add_server(self, config: models.MCPServerConfig): - """Add and start a new MCP server from a configuration object.""" + """ + Add and start a new MCP server from a configuration object. + [C: tests/test_external_mcp.py:test_external_mcp_real_process, tests/test_external_mcp.py:test_get_tool_schemas_includes_external] + """ if config.url: # RemoteMCPServer placeholder return @@ -1225,13 +1268,19 @@ class ExternalMCPManager: self.servers[config.name] = server async def stop_all(self): - """Stop all managed MCP servers and clear the registry.""" + """ + Stop all managed MCP servers and clear the registry. + [C: tests/test_external_mcp.py:test_external_mcp_real_process, tests/test_external_mcp.py:test_get_tool_schemas_includes_external, tests/test_external_mcp_e2e.py:test_external_mcp_e2e_refresh_and_call] + """ for server in self.servers.values(): await server.stop() self.servers = {} def get_all_tools(self) -> dict: - """Retrieve a dictionary of all tools available across all managed servers.""" + """ + Retrieve a dictionary of all tools available across all managed servers. + [C: tests/test_external_mcp.py:test_external_mcp_real_process, tests/test_external_mcp_e2e.py:test_external_mcp_e2e_refresh_and_call] + """ all_tools = {} for sname, server in self.servers.items(): for tname, tool in server.tools.items(): @@ -1243,7 +1292,10 @@ class ExternalMCPManager: return {name: server.status for name, server in self.servers.items()} async def async_dispatch(self, tool_name: str, tool_input: dict) -> str: - """Dispatch a tool call to the appropriate external MCP server asynchronously.""" + """ + Dispatch a tool call to the appropriate external MCP server asynchronously. + [C: src/rag_engine.py:RAGEngine._async_search_mcp, tests/test_external_mcp.py:test_external_mcp_real_process] + """ for server in self.servers.values(): if tool_name in server.tools: return await server.call_tool(tool_name, tool_input) @@ -1252,14 +1304,19 @@ class ExternalMCPManager: _external_mcp_manager = ExternalMCPManager() def get_external_mcp_manager() -> ExternalMCPManager: - """Retrieve the global ExternalMCPManager instance.""" + """ + Retrieve the global ExternalMCPManager instance. + [C: tests/test_external_mcp.py:test_get_tool_schemas_includes_external, tests/test_external_mcp_e2e.py:test_external_mcp_e2e_refresh_and_call] + """ global _external_mcp_manager return _external_mcp_manager def dispatch(tool_name: str, tool_input: dict[str, Any]) -> str: """ - Dispatch an MCP tool call by name. Returns the result as a string. - """ + + Dispatch an MCP tool call by name. Returns the result as a string. + [C: tests/test_gemini_cli_edge_cases.py:test_gemini_cli_parameter_resilience, tests/test_mcp_client_beads.py:test_bd_mcp_tools, tests/test_mcp_ts_integration.py:test_ts_c_get_code_outline_dispatch, tests/test_mcp_ts_integration.py:test_ts_c_get_definition_dispatch, tests/test_mcp_ts_integration.py:test_ts_c_get_signature_dispatch, tests/test_mcp_ts_integration.py:test_ts_c_get_skeleton_dispatch, tests/test_mcp_ts_integration.py:test_ts_c_update_definition_dispatch, tests/test_mcp_ts_integration.py:test_ts_cpp_get_code_outline_dispatch, tests/test_mcp_ts_integration.py:test_ts_cpp_get_definition_dispatch, tests/test_mcp_ts_integration.py:test_ts_cpp_get_signature_dispatch, tests/test_mcp_ts_integration.py:test_ts_cpp_get_skeleton_dispatch, tests/test_mcp_ts_integration.py:test_ts_cpp_update_definition_dispatch] + """ # Handle aliases path = str(tool_input.get("path", tool_input.get("file_path", tool_input.get("dir_path", "")))) if tool_name == "read_file": @@ -1377,6 +1434,9 @@ def dispatch(tool_name: str, tool_input: dict[str, Any]) -> str: async def async_dispatch(tool_name: str, tool_input: dict[str, Any]) -> str: # Check native tools + """ + [C: src/rag_engine.py:RAGEngine._async_search_mcp, tests/test_external_mcp.py:test_external_mcp_real_process] + """ native_names = {t['name'] for t in MCP_TOOL_SPECS} if tool_name in native_names: return await asyncio.to_thread(dispatch, tool_name, tool_input) @@ -1390,6 +1450,9 @@ async def async_dispatch(tool_name: str, tool_input: dict[str, Any]) -> str: def get_tool_schemas() -> list[dict[str, Any]]: + """ + [C: tests/test_arch_boundary_phase2.py:TestArchBoundaryPhase2.test_mcp_client_dispatch_completeness, tests/test_external_mcp.py:test_get_tool_schemas_includes_external, tests/test_mcp_client_beads.py:test_bd_mcp_tools] + """ res = list(MCP_TOOL_SPECS) manager = get_external_mcp_manager() for tname, tinfo in manager.get_all_tools().items(): @@ -2126,4 +2189,3 @@ TOOL_NAMES: set[str] = {t['name'] for t in MCP_TOOL_SPECS} - diff --git a/src/models.py b/src/models.py index fae1827..bf148b2 100644 --- a/src/models.py +++ b/src/models.py @@ -58,6 +58,9 @@ def _clean_nones(data: Any) -> Any: return data def load_config() -> dict[str, Any]: + """ + [C: src/multi_agent_conductor.py:ConductorEngine.__init__] + """ with open(CONFIG_PATH, "rb") as f: return tomllib.load(f) @@ -164,10 +167,16 @@ class ThinkingSegment: marker: str # 'thinking', 'thought', or 'Thinking:' def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return {"content": self.content, "marker": self.marker} @classmethod def from_dict(cls, data: Dict[str, Any]) -> "ThinkingSegment": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return cls(content=data["content"], marker=data["marker"]) @@ -190,27 +199,45 @@ class Ticket: persona_id: Optional[str] = None def mark_blocked(self, reason: str) -> None: + """ + [C: src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_mma_models.py:test_ticket_mark_blocked] + """ self.status = "blocked" self.blocked_reason = reason def mark_manual_block(self, reason: str) -> None: + """ + [C: tests/test_manual_block.py:test_clear_manual_block_method, tests/test_manual_block.py:test_mark_manual_block_method] + """ self.status = "blocked" self.blocked_reason = f"[MANUAL] {reason}" self.manual_block = True def clear_manual_block(self) -> None: + """ + [C: tests/test_manual_block.py:test_clear_manual_block_method] + """ if self.manual_block: self.status = "todo" self.blocked_reason = None self.manual_block = False def mark_complete(self) -> None: + """ + [C: src/multi_agent_conductor.py:run_worker_lifecycle, tests/test_conductor_engine_v2.py:do_work, tests/test_mma_models.py:test_ticket_mark_complete, tests/test_mma_models.py:test_track_get_executable_tickets_complex] + """ self.status = "completed" def get(self, key: str, default: Any = None) -> Any: + """ + [C: simulation/live_walkthrough.py:main, simulation/ping_pong.py:main, simulation/sim_context.py:ContextSimulation.run, simulation/sim_execution.py:ExecutionSimulation.run, simulation/sim_tools.py:ToolsSimulation.run, simulation/user_agent.py:UserSimAgent.generate_response, simulation/workflow_sim.py:WorkflowSimulator.run_discussion_turn_async, simulation/workflow_sim.py:WorkflowSimulator.wait_for_ai_response, src/multi_agent_conductor.py:ConductorEngine.__init__, src/multi_agent_conductor.py:ConductorEngine.kill_worker, src/multi_agent_conductor.py:ConductorEngine.parse_json_tickets, src/multi_agent_conductor.py:ConductorEngine.run, src/multi_agent_conductor.py:clutch_callback, src/multi_agent_conductor.py:confirm_spawn, src/multi_agent_conductor.py:run_worker_lifecycle, src/multi_agent_conductor.py:worker_comms_callback, src/native_orchestrator.py:NativeOrchestrator.execute_ticket, src/orchestrator_pm.py:generate_tracks, src/orchestrator_pm.py:get_track_history_summary, src/orchestrator_pm.py:module, src/paths.py:_get_project_conductor_dir_from_toml, src/paths.py:get_config_path, src/paths.py:get_global_personas_path, src/paths.py:get_global_presets_path, src/paths.py:get_global_tool_presets_path, src/paths.py:get_global_workspace_profiles_path, src/performance_monitor.py:PerformanceMonitor._get_avg, src/performance_monitor.py:PerformanceMonitor.end_component, src/performance_monitor.py:PerformanceMonitor.get_metrics, src/personas.py:PersonaManager.get_persona_scope, src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.delete_preset, src/presets.py:PresetManager.get_preset_scope, src/presets.py:PresetManager.load_all, src/project_manager.py:branch_discussion, src/project_manager.py:entry_to_str, src/project_manager.py:flat_config, src/project_manager.py:get_all_tracks, src/project_manager.py:load_track_history, src/project_manager.py:migrate_from_legacy_config, src/project_manager.py:promote_take, src/rag_engine.py:RAGEngine.get_all_indexed_paths, src/rag_engine.py:RAGEngine.index_file, src/shell_runner.py:_build_subprocess_env, src/shell_runner.py:_load_env_config, src/summarize.py:build_summary_markdown, src/summarize.py:summarise_file, src/summarize.py:summarise_items, src/summary_cache.py:SummaryCache.get_summary, src/synthesis_formatter.py:format_takes_diff, src/theme.py:apply, src/theme.py:get_palette_colours, src/theme.py:get_shader_config, src/theme.py:get_window_frame_config, src/theme.py:load_from_config, src/theme_2.py:load_from_config, src/tool_bias.py:ToolBiasEngine.apply_semantic_nudges, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/conftest.py:live_gui, tests/mock_gemini_cli.py:main, 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_async_tools.py:mocked_async_dispatch, tests/test_auto_switch_sim.py:test_auto_switch_sim, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_allow_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_deny_decision, tests/test_cli_tool_bridge.py:TestCliToolBridge.test_unreachable_hook_server, tests/test_cli_tool_bridge_mapping.py:TestCliToolBridgeMapping.test_mapping_from_api_format, tests/test_conductor_api_hook_integration.py:simulate_conductor_phase_completion, tests/test_conductor_engine_v2.py:mock_open_side_effect, tests/test_conductor_engine_v2.py:mock_send_side_effect, 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_gemini_cli_adapter.py:TestGeminiCliAdapter.test_send_captures_usage_metadata, tests/test_gui2_performance.py:test_performance_benchmarking, tests/test_gui_context_presets.py:test_gui_context_preset_save_load, tests/test_gui_events_v2.py:test_sync_event_queue, tests/test_gui_performance_requirements.py:test_idle_performance_requirements, tests/test_gui_phase4.py:test_delete_ticket_logic, tests/test_gui_stress_performance.py:test_comms_volume_stress_performance, tests/test_gui_text_viewer.py:test_text_viewer_state_update, tests/test_gui_updates.py:test_gui_updates_on_event, tests/test_headless_service.py:TestHeadlessAPI.test_endpoint_no_api_key_configured, tests/test_headless_service.py:TestHeadlessAPI.test_get_context_endpoint, tests/test_headless_service.py:TestHeadlessAPI.test_health_endpoint, tests/test_headless_service.py:TestHeadlessAPI.test_list_sessions_endpoint, tests/test_headless_service.py:TestHeadlessAPI.test_pending_actions_endpoint, tests/test_headless_service.py:TestHeadlessAPI.test_status_endpoint_authorized, tests/test_headless_service.py:TestHeadlessAPI.test_status_endpoint_unauthorized, tests/test_live_gui_integration_v2.py:test_api_gui_state_live, 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_live_workflow.py:test_full_live_workflow, tests/test_live_workflow.py:wait_for_value, tests/test_log_registry.py:TestLogRegistry.test_register_session, tests/test_log_registry.py:TestLogRegistry.test_update_session_metadata, tests/test_mma_agent_focus_phase3.py:test_comms_log_filter_not_applied_for_prior_session, tests/test_mma_agent_focus_phase3.py:test_comms_log_filter_tier3_only, tests/test_mma_agent_focus_phase3.py:test_tool_log_filter_all, tests/test_mma_agent_focus_phase3.py:test_tool_log_filter_excludes_none_tier, tests/test_mma_agent_focus_phase3.py:test_tool_log_filter_tier3_only, tests/test_mma_approval_indicators.py:_make_app, tests/test_mma_concurrent_tracks_sim.py:test_mma_concurrent_tracks_execution, tests/test_mma_concurrent_tracks_stress_sim.py:_poll_mma_workers, tests/test_mma_concurrent_tracks_stress_sim.py:test_mma_concurrent_tracks_stress, tests/test_mma_dashboard_streams.py:_make_app, tests/test_mma_orchestration_gui.py:test_handle_ai_response_with_stream_id, 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_mock_gemini_cli.py:get_message_content, 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_per_ticket_model.py:test_model_override_serialization, tests/test_phase6_engine.py:test_worker_streaming_intermediate, tests/test_preset_windows_layout.py:test_api_hook_under_load, tests/test_preset_windows_layout.py:test_preset_windows_opening, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, 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_switching, tests/test_selectable_ui.py:test_selectable_label_stability, tests/test_sim_ai_settings.py:side_effect, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:side_effect, tests/test_spawn_interception_v2.py:test_confirm_spawn_pushed_to_queue, tests/test_status_encapsulation.py:test_status_properties, tests/test_sync_events.py:test_sync_event_queue_multiple, tests/test_sync_events.py:test_sync_event_queue_none_payload, tests/test_sync_events.py:test_sync_event_queue_put_get, tests/test_task_dag_popout_sim.py:test_task_dag_popout, tests/test_tier4_interceptor.py:test_ai_client_passes_qa_callback, tests/test_tiered_aggregation.py:test_app_controller_do_generate_uses_persona_strategy, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_token_usage.py:test_token_usage_tracking, tests/test_tool_management_layout.py:test_tool_management_state_updates, tests/test_tool_preset_env.py:test_tool_preset_env_loading, tests/test_tool_preset_env.py:test_tool_preset_env_no_var, tests/test_tool_presets_sim.py:test_tool_preset_switching, tests/test_ui_cache_controls_sim.py:test_ui_cache_controls, tests/test_ui_summary_only_removal.py:test_project_without_summary_only_loads, tests/test_usage_analytics_popout_sim.py:test_usage_analytics_popout, tests/test_visual_orchestration.py:test_mma_epic_lifecycle, tests/test_visual_sim_gui_ux.py:test_gui_track_creation, tests/test_visual_sim_gui_ux.py:test_gui_ux_event_routing, tests/test_visual_sim_mma_v2.py:_drain_approvals, tests/test_visual_sim_mma_v2.py:_mma_active, tests/test_visual_sim_mma_v2.py:_poll, tests/test_visual_sim_mma_v2.py:_tier3_in_streams, tests/test_visual_sim_mma_v2.py:_tier3_usage_nonzero, tests/test_visual_sim_mma_v2.py:_track_loaded, tests/test_visual_sim_mma_v2.py:test_mma_complete_lifecycle] + """ return getattr(self, key, default) def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return { "id": self.id, "description": self.description, @@ -231,6 +258,9 @@ class Ticket: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "Ticket": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return cls( id=data["id"], description=data.get("description", ""), @@ -257,11 +287,17 @@ class Track: tickets: List[Ticket] = field(default_factory=list) def get_executable_tickets(self) -> List[Ticket]: + """ + [C: tests/test_mma_models.py:test_track_get_executable_tickets, tests/test_mma_models.py:test_track_get_executable_tickets_complex] + """ from src.dag_engine import TrackDAG dag = TrackDAG(self.tickets) return dag.get_ready_tasks() def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return { "id": self.id, "description": self.description, @@ -270,6 +306,9 @@ class Track: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "Track": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return cls( id=data["id"], description=data.get("description", ""), @@ -297,6 +336,9 @@ class Metadata: updated_at: Optional[datetime.datetime] = None def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return { "id": self.id, "name": self.name, @@ -307,6 +349,9 @@ class Metadata: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "Metadata": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ created = data.get("created_at") updated = data.get("updated_at") if isinstance(created, str): @@ -335,6 +380,9 @@ class TextEditorConfig: diff_args: List[str] = field(default_factory=list) def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return { "name": self.name, "path": self.path, @@ -343,6 +391,9 @@ class TextEditorConfig: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "TextEditorConfig": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return cls( name=data["name"], path=data["path"], @@ -356,6 +407,9 @@ class ExternalEditorConfig: default_editor: Optional[str] = None def get_default(self) -> Optional[TextEditorConfig]: + """ + [C: tests/test_external_editor.py:TestExternalEditorConfig.test_get_default_fallback_to_first, tests/test_external_editor.py:TestExternalEditorConfig.test_get_default_returns_configured, tests/test_external_editor.py:TestExternalEditorConfig.test_get_default_returns_none_when_empty] + """ if self.default_editor and self.default_editor in self.editors: return self.editors[self.default_editor] if self.editors: @@ -363,6 +417,9 @@ class ExternalEditorConfig: return None def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return { "editors": {k: v.to_dict() for k, v in self.editors.items()}, "default_editor": self.default_editor, @@ -370,6 +427,9 @@ class ExternalEditorConfig: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "ExternalEditorConfig": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ editors = {} for name, ed_data in data.get("editors", {}).items(): if isinstance(ed_data, dict): @@ -389,6 +449,9 @@ class TextEditorConfig: diff_args: List[str] = field(default_factory=list) def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return { "name": self.name, "path": self.path, @@ -397,6 +460,9 @@ class TextEditorConfig: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "TextEditorConfig": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return cls( name=data["name"], path=data["path"], @@ -410,6 +476,9 @@ class ExternalEditorConfig: default_editor: Optional[str] = None def get_default(self) -> Optional[TextEditorConfig]: + """ + [C: tests/test_external_editor.py:TestExternalEditorConfig.test_get_default_fallback_to_first, tests/test_external_editor.py:TestExternalEditorConfig.test_get_default_returns_configured, tests/test_external_editor.py:TestExternalEditorConfig.test_get_default_returns_none_when_empty] + """ if self.default_editor and self.default_editor in self.editors: return self.editors[self.default_editor] if self.editors: @@ -417,6 +486,9 @@ class ExternalEditorConfig: return None def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return { "editors": {k: v.to_dict() for k, v in self.editors.items()}, "default_editor": self.default_editor, @@ -424,6 +496,9 @@ class ExternalEditorConfig: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "ExternalEditorConfig": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ editors = {} for name, ed_data in data.get("editors", {}).items(): if isinstance(ed_data, dict): @@ -447,6 +522,9 @@ class TrackState: tasks: List[Ticket] = field(default_factory=list) def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ serialized_discussion = [] for item in self.discussion: if isinstance(item, dict): @@ -464,6 +542,9 @@ class TrackState: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "TrackState": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ discussion = data.get("discussion", []) parsed_discussion = [] for item in discussion: @@ -492,6 +573,9 @@ class FileItem: injected_at: Optional[float] = None def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return { "path": self.path, "auto_aggregate": self.auto_aggregate, @@ -501,6 +585,9 @@ class FileItem: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "FileItem": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return cls( path=data["path"], auto_aggregate=data.get("auto_aggregate", True), @@ -514,12 +601,18 @@ class Preset: system_prompt: str def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return { "system_prompt": self.system_prompt, } @classmethod def from_dict(cls, name: str, data: Dict[str, Any]) -> "Preset": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return cls( name=name, system_prompt=data.get("system_prompt", ""), @@ -533,6 +626,9 @@ class Tool: parameter_bias: Dict[str, str] = field(default_factory=dict) def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return { "name": self.name, "approval": self.approval, @@ -542,6 +638,9 @@ class Tool: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "Tool": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return cls( name=data["name"], approval=data.get("approval", "auto"), @@ -555,6 +654,9 @@ class ToolPreset: categories: Dict[str, List[Union[Tool, Any]]] def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ serialized_categories = {} for cat, tools in self.categories.items(): serialized_categories[cat] = [t.to_dict() if isinstance(t, Tool) else t for t in tools] @@ -564,6 +666,9 @@ class ToolPreset: @classmethod def from_dict(cls, name: str, data: Dict[str, Any]) -> "ToolPreset": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ raw_categories = data.get("categories", {}) parsed_categories = {} for cat, tools in raw_categories.items(): @@ -580,6 +685,9 @@ class BiasProfile: category_multipliers: Dict[str, float] = field(default_factory=dict) def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return { "name": self.name, "tool_weights": self.tool_weights, @@ -588,6 +696,9 @@ class BiasProfile: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "BiasProfile": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return cls( name=data["name"], tool_weights=data.get("tool_weights", {}), @@ -630,6 +741,9 @@ class Persona: return self.preferred_models[0].get("max_output_tokens") def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ res = { "system_prompt": self.system_prompt, } @@ -653,6 +767,9 @@ class Persona: @classmethod def from_dict(cls, name: str, data: Dict[str, Any]) -> "Persona": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ raw_models = data.get("preferred_models", []) parsed_models = [] for m in raw_models: @@ -694,6 +811,9 @@ class MCPServerConfig: auto_start: bool = False def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ res = {'auto_start': self.auto_start} if self.command: res['command'] = self.command if self.args: res['args'] = self.args @@ -702,6 +822,9 @@ class MCPServerConfig: @classmethod def from_dict(cls, name: str, data: Dict[str, Any]) -> 'MCPServerConfig': + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return cls( name=name, command=data.get('command'), @@ -715,12 +838,18 @@ class MCPConfiguration: mcpServers: Dict[str, MCPServerConfig] = field(default_factory=dict) def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return { 'mcpServers': {name: cfg.to_dict() for name, cfg in self.mcpServers.items()} } @classmethod def from_dict(cls, data: Dict[str, Any]) -> 'MCPConfiguration': + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ raw_servers = data.get('mcpServers', {}) parsed_servers = { name: MCPServerConfig.from_dict(name, cfg) @@ -738,6 +867,9 @@ class VectorStoreConfig: mcp_tool: Optional[str] = None def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return { "provider": self.provider, "url": self.url, @@ -749,6 +881,9 @@ class VectorStoreConfig: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "VectorStoreConfig": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return cls( provider=data["provider"], url=data.get("url"), @@ -767,6 +902,9 @@ class RAGConfig: chunk_overlap: int = 200 def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return { "enabled": self.enabled, "vector_store": self.vector_store.to_dict(), @@ -777,6 +915,9 @@ class RAGConfig: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "RAGConfig": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return cls( enabled=data.get("enabled", False), vector_store=VectorStoreConfig.from_dict(data.get("vector_store", {"provider": "mock"})), @@ -793,6 +934,9 @@ class WorkspaceProfile: panel_states: Dict[str, Any] def to_dict(self) -> Dict[str, Any]: + """ + [C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return { "ini_content": self.ini_content, "show_windows": self.show_windows, @@ -801,6 +945,9 @@ class WorkspaceProfile: @classmethod def from_dict(cls, name: str, data: Dict[str, Any]) -> "WorkspaceProfile": + """ + [C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags] + """ return cls( name=name, ini_content=data.get("ini_content", ""), @@ -809,6 +956,9 @@ class WorkspaceProfile: ) def load_mcp_config(path: str) -> MCPConfiguration: + """ + [C: tests/test_mcp_config.py:test_load_mcp_config] + """ if not os.path.exists(path): return MCPConfiguration() with open(path, 'r', encoding='utf-8') as f: @@ -817,4 +967,3 @@ def load_mcp_config(path: str) -> MCPConfiguration: return MCPConfiguration.from_dict(data) except Exception: return MCPConfiguration() - diff --git a/src/multi_agent_conductor.py b/src/multi_agent_conductor.py index 7b91486..5249618 100644 --- a/src/multi_agent_conductor.py +++ b/src/multi_agent_conductor.py @@ -47,7 +47,8 @@ from src.dag_engine import TrackDAG, ExecutionEngine class WorkerPool: """ - Manages a pool of worker threads with a concurrency limit. + + Manages a pool of worker threads with a concurrency limit. """ def __init__(self, max_workers: int = 4): self.max_workers = max_workers @@ -57,8 +58,10 @@ class WorkerPool: def spawn(self, ticket_id: str, target: Callable, args: tuple) -> Optional[threading.Thread]: """ - Spawns a new worker thread if the pool is not full. - Returns the thread object or None if full. + + Spawns a new worker thread if the pool is not full. + Returns the thread object or None if full. + [C: tests/test_parallel_execution.py:test_worker_pool_completion_cleanup, tests/test_parallel_execution.py:test_worker_pool_limit, tests/test_parallel_execution.py:test_worker_pool_tracking] """ with self._lock: if len(self._active) >= self.max_workers: @@ -79,6 +82,9 @@ class WorkerPool: return t def join_all(self, timeout: float = None) -> None: + """ + [C: tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_parallel_execution.py:test_worker_pool_limit, tests/test_parallel_execution.py:test_worker_pool_tracking] + """ with self._lock: threads = list(self._active.values()) for t in threads: @@ -87,16 +93,23 @@ class WorkerPool: self._active.clear() def get_active_count(self) -> int: + """ + [C: tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_parallel_execution.py:test_worker_pool_completion_cleanup, tests/test_parallel_execution.py:test_worker_pool_limit] + """ with self._lock: return len(self._active) def is_full(self) -> bool: + """ + [C: tests/test_parallel_execution.py:test_worker_pool_limit] + """ return self.get_active_count() >= self.max_workers class ConductorEngine: """ - Orchestrates the execution of tickets within a track. - """ + + Orchestrates the execution of tickets within a track. + """ def __init__(self, track: Track, event_queue: Optional[events.AsyncEventQueue] = None, auto_queue: bool = False) -> None: self.track = track @@ -134,25 +147,40 @@ class ConductorEngine: self.tier_usage[tier]["output"] += output_tokens def pause(self) -> None: - """Pauses the pipeline execution.""" + """ + Pauses the pipeline execution. + [C: tests/test_pipeline_pause.py:test_pause_method, tests/test_pipeline_pause.py:test_resume_method] + """ self._pause_event.set() def resume(self) -> None: - """Resumes the pipeline execution.""" + """ + Resumes the pipeline execution. + [C: tests/test_pipeline_pause.py:test_resume_method] + """ self._pause_event.clear() def approve_task(self, task_id: str) -> None: - """Manually transition todo to in_progress and mark engine dirty.""" + """ + Manually transition todo to in_progress and mark engine dirty. + [C: tests/test_execution_engine.py:test_execution_engine_approve_task, tests/test_execution_engine.py:test_execution_engine_step_mode] + """ self.engine.approve_task(task_id) self._dirty = True def update_task_status(self, task_id: str, status: str) -> None: - """Force-update ticket status and mark engine dirty.""" + """ + Force-update ticket status and mark engine dirty. + [C: tests/test_arch_boundary_phase3.py:TestArchBoundaryPhase3.test_manual_unblock_restores_todo, tests/test_execution_engine.py:test_execution_engine_auto_queue, tests/test_execution_engine.py:test_execution_engine_basic_flow, tests/test_execution_engine.py:test_execution_engine_status_persistence, tests/test_execution_engine.py:test_execution_engine_update_nonexistent_task] + """ self.engine.update_task_status(task_id, status) self._dirty = True def kill_worker(self, ticket_id: str) -> None: - """Sets the abort event for a worker and attempts to join its thread.""" + """ + Sets the abort event for a worker and attempts to join its thread. + [C: tests/test_conductor_engine_abort.py:test_kill_worker_sets_abort_and_joins_thread] + """ if ticket_id in self._abort_events: print(f"[MMA] Setting abort event for {ticket_id}") self._abort_events[ticket_id].set() @@ -184,9 +212,11 @@ class ConductorEngine: def parse_json_tickets(self, json_str: str) -> None: """ - Parses a JSON string of ticket definitions (Godot ECS Flat List format) - and populates the Track's ticket list. - """ + + Parses a JSON string of ticket definitions (Godot ECS Flat List format) + and populates the Track's ticket list. + [C: tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_orchestration_logic.py:test_conductor_engine_parse_json_tickets] + """ try: data = json.loads(json_str) if not isinstance(data, list): @@ -213,11 +243,13 @@ class ConductorEngine: def run(self, md_content: str = "", max_ticks: Optional[int] = None) -> None: """ - Main execution loop using the DAG engine. - Args: - md_content: The full markdown context (history + files) for AI workers. - max_ticks: Optional limit on number of iterations (for testing). - """ + + Main execution loop using the DAG engine. + Args: + md_content: The full markdown context (history + files) for AI workers. + max_ticks: Optional limit on number of iterations (for testing). + [C: simulation/sim_base.py:run_sim, src/project_manager.py:get_git_commit, src/project_manager.py:get_git_log, src/rag_engine.py:RAGEngine._search_mcp, src/shell_runner.py:run_powershell, tests/conftest.py:kill_process_tree, tests/conftest.py:live_gui, tests/test_conductor_abort_event.py:test_conductor_abort_event_populated, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:get_vscode_processes, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_custom_window.py:test_app_window_is_borderless, tests/test_headless_simulation.py:module, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_mock_gemini_cli.py:run_mock, tests/test_orchestration_logic.py:test_conductor_engine_run, tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:test_execution_simulation_run, tests/test_sim_tools.py:test_tools_simulation_run] + """ tick_count = 0 while True: if self._pause_event.is_set(): @@ -335,8 +367,9 @@ def _queue_put(event_queue: events.AsyncEventQueue, event_name: str, payload) -> def confirm_execution(payload: str, event_queue: events.AsyncEventQueue, ticket_id: str) -> bool: """ - Pushes an approval request to the GUI and waits for response. - """ + + Pushes an approval request to the GUI and waits for response. + """ dialog_container = [None] task = { "action": "mma_step_approval", @@ -357,9 +390,11 @@ def confirm_execution(payload: str, event_queue: events.AsyncEventQueue, ticket_ def confirm_spawn(role: str, prompt: str, context_md: str, event_queue: events.AsyncEventQueue, ticket_id: str) -> Tuple[bool, str, str]: """ - Pushes a spawn approval request to the GUI and waits for response. - Returns (approved, modified_prompt, modified_context) - """ + + Pushes a spawn approval request to the GUI and waits for response. + Returns (approved, modified_prompt, modified_context) + [C: tests/test_spawn_interception_v2.py:run_confirm] + """ dialog_container = [None] task = { "action": "mma_spawn_approval", @@ -396,16 +431,18 @@ def confirm_spawn(role: str, prompt: str, context_md: str, event_queue: events.A def run_worker_lifecycle(ticket: Ticket, context: WorkerContext, context_files: List[str] | None = None, event_queue: events.AsyncEventQueue | None = None, engine: Optional['ConductorEngine'] = None, md_content: str = "") -> None: """ - Simulates the lifecycle of a single agent working on a ticket. - Calls the AI client and updates the ticket status based on the response. - Args: - ticket: The ticket to process. - context: The worker context. - context_files: List of files to include in the context. - event_queue: Queue for pushing state updates and receiving approvals. - engine: The conductor engine. - md_content: The markdown context (history + files) for AI workers. - """ + + Simulates the lifecycle of a single agent working on a ticket. + Calls the AI client and updates the ticket status based on the response. + Args: + ticket: The ticket to process. + context: The worker context. + context_files: List of files to include in the context. + event_queue: Queue for pushing state updates and receiving approvals. + engine: The conductor engine. + md_content: The markdown context (history + files) for AI workers. + [C: tests/test_conductor_engine_v2.py:test_run_worker_lifecycle_calls_ai_client_send, tests/test_conductor_engine_v2.py:test_run_worker_lifecycle_context_injection, tests/test_conductor_engine_v2.py:test_run_worker_lifecycle_handles_blocked_response, tests/test_conductor_engine_v2.py:test_run_worker_lifecycle_pushes_response_via_queue, tests/test_conductor_engine_v2.py:test_run_worker_lifecycle_step_mode_confirmation, tests/test_conductor_engine_v2.py:test_run_worker_lifecycle_step_mode_rejection, tests/test_conductor_engine_v2.py:test_run_worker_lifecycle_token_usage_from_comms_log, tests/test_context_pruner.py:test_token_reduction_logging, tests/test_orchestration_logic.py:test_run_worker_lifecycle_blocked, tests/test_phase6_engine.py:test_worker_streaming_intermediate, tests/test_run_worker_lifecycle_abort.py:TestRunWorkerLifecycleAbort.test_run_worker_lifecycle_returns_early_on_abort, tests/test_spawn_interception_v2.py:test_run_worker_lifecycle_approved, tests/test_spawn_interception_v2.py:test_run_worker_lifecycle_rejected, tests/test_tiered_aggregation.py:test_run_worker_lifecycle_uses_strategy] + """ # Enforce Context Amnesia: each ticket starts with a clean slate. ai_client.reset_session() ai_client.set_provider(ai_client.get_provider(), context.model_name) @@ -609,4 +646,3 @@ def run_worker_lifecycle(ticket: Ticket, context: WorkerContext, context_files: if event_queue: _queue_put(event_queue, "ticket_completed", {"ticket_id": ticket.id, "timestamp": time.time()}) return response - diff --git a/src/native_orchestrator.py b/src/native_orchestrator.py index 00fe72d..e56ebed 100644 --- a/src/native_orchestrator.py +++ b/src/native_orchestrator.py @@ -9,20 +9,29 @@ import re from src import paths def read_plan(track_id: str, base_dir: str = ".") -> str: - """Reads the implementation plan (plan.md) for a track.""" + """ + Reads the implementation plan (plan.md) for a track. + [C: tests/test_native_orchestrator.py:test_read_plan_nonexistent, tests/test_native_orchestrator.py:test_write_and_read_plan] + """ plan_path = paths.get_track_state_dir(track_id, base_dir) / "plan.md" if not plan_path.exists(): return "" return plan_path.read_text(encoding="utf-8") def write_plan(track_id: str, content: str, base_dir: str = ".") -> None: - """Writes the implementation plan (plan.md) for a track.""" + """ + Writes the implementation plan (plan.md) for a track. + [C: tests/test_native_orchestrator.py:test_write_and_read_plan] + """ plan_path = paths.get_track_state_dir(track_id, base_dir) / "plan.md" plan_path.parent.mkdir(parents=True, exist_ok=True) plan_path.write_text(content, encoding="utf-8") def parse_plan_tasks(content: str) -> list[dict[str, str]]: - """Parses the tasks from a plan.md file.""" + """ + Parses the tasks from a plan.md file. + [C: tests/test_native_orchestrator.py:test_parse_plan_tasks] + """ tasks = [] for line in content.split("\n"): stripped = line.strip() @@ -33,24 +42,36 @@ def parse_plan_tasks(content: str) -> list[dict[str, str]]: return tasks def read_metadata(track_id: str, base_dir: str = ".") -> dict: - """Reads the metadata (metadata.json) for a track.""" + """ + Reads the metadata (metadata.json) for a track. + [C: tests/test_native_orchestrator.py:test_read_metadata_nonexistent, tests/test_native_orchestrator.py:test_write_and_read_metadata] + """ meta_path = paths.get_track_state_dir(track_id, base_dir) / "metadata.json" if not meta_path.exists(): return {} return json.loads(meta_path.read_text(encoding="utf-8")) def write_metadata(track_id: str, data: dict, base_dir: str = ".") -> None: - """Writes the metadata (metadata.json) for a track.""" + """ + Writes the metadata (metadata.json) for a track. + [C: tests/test_native_orchestrator.py:test_write_and_read_metadata] + """ meta_path = paths.get_track_state_dir(track_id, base_dir) / "metadata.json" meta_path.parent.mkdir(parents=True, exist_ok=True) meta_path.write_text(json.dumps(data, indent=2), encoding="utf-8") def get_track_dir(track_id: str, base_dir: str = ".") -> Path: - """Returns the state directory for a specific track.""" + """ + Returns the state directory for a specific track. + [C: tests/test_native_orchestrator.py:test_get_track_dir] + """ return paths.get_track_state_dir(track_id, base_dir) def get_archive_dir(base_dir: str = ".") -> Path: - """Returns the central archive directory for completed tracks.""" + """ + Returns the central archive directory for completed tracks. + [C: src/orchestrator_pm.py:get_track_history_summary, tests/test_native_orchestrator.py:test_get_archive_dir, tests/test_paths.py:test_conductor_dir_project_relative] + """ return paths.get_archive_dir(base_dir) class NativeOrchestrator: @@ -59,28 +80,46 @@ class NativeOrchestrator: self._conductor = None def load_track(self, track_id: str) -> dict: - """Load track from metadata.json""" + """ + Load track from metadata.json + [C: tests/test_native_orchestrator.py:test_native_orchestrator_class] + """ return read_metadata(track_id, str(self.base_dir)) def save_track(self, track_id: str, data: dict) -> None: - """Persist track metadata""" + """ + Persist track metadata + [C: tests/test_native_orchestrator.py:test_native_orchestrator_class] + """ write_metadata(track_id, data, str(self.base_dir)) def load_plan(self, track_id: str) -> str: - """Load plan.md content""" + """ + Load plan.md content + [C: tests/test_native_orchestrator.py:test_native_orchestrator_class] + """ return read_plan(track_id, str(self.base_dir)) def save_plan(self, track_id: str, content: str) -> None: - """Persist plan.md content""" + """ + Persist plan.md content + [C: tests/test_native_orchestrator.py:test_native_orchestrator_class] + """ write_plan(track_id, content, str(self.base_dir)) def get_track_tasks(self, track_id: str) -> list[dict]: - """Get parsed task list from plan.md""" + """ + Get parsed task list from plan.md + [C: tests/test_native_orchestrator.py:test_native_orchestrator_class] + """ content = self.load_plan(track_id) return parse_plan_tasks(content) def generate_tickets(self, brief: str, module_skeletons: str = "") -> list[dict]: - """Tier 2: Generate tickets from brief""" + """ + Tier 2: Generate tickets from brief + [C: tests/test_conductor_tech_lead.py:TestConductorTechLead.test_generate_tickets_retry_failure, tests/test_conductor_tech_lead.py:TestConductorTechLead.test_generate_tickets_retry_success, tests/test_conductor_tech_lead.py:TestConductorTechLead.test_generate_tickets_success, tests/test_orchestration_logic.py:test_generate_tickets] + """ from src import conductor_tech_lead return conductor_tech_lead.generate_tickets(brief, module_skeletons) @@ -98,4 +137,3 @@ class NativeOrchestrator: """Tier 4: Generate patch for error""" from src import ai_client return ai_client.run_tier4_patch_generation(error, file_context) - diff --git a/src/orchestrator_pm.py b/src/orchestrator_pm.py index a64ee3e..8fb6203 100644 --- a/src/orchestrator_pm.py +++ b/src/orchestrator_pm.py @@ -11,8 +11,10 @@ from src import paths def get_track_history_summary() -> str: """ - Scans conductor/archive/ and conductor/tracks/ to build a summary of past work. - """ + + Scans conductor/archive/ and conductor/tracks/ to build a summary of past work. + [C: tests/test_orchestrator_pm_history.py:TestOrchestratorPMHistory.test_get_track_history_summary, tests/test_orchestrator_pm_history.py:TestOrchestratorPMHistory.test_get_track_history_summary_missing_files] + """ summary_parts = [] archive_path = paths.get_archive_dir() tracks_path = paths.get_tracks_dir() @@ -56,9 +58,11 @@ def get_track_history_summary() -> str: def generate_tracks(user_request: str, project_config: dict[str, Any], file_items: list[dict[str, Any]], history_summary: Optional[str] = None) -> list[dict[str, Any]]: """ - Tier 1 (Strategic PM) call. - Analyzes the project state and user request to generate a list of Tracks. - """ + + Tier 1 (Strategic PM) call. + Analyzes the project state and user request to generate a list of Tracks. + [C: tests/test_orchestration_logic.py:test_generate_tracks, tests/test_orchestrator_pm.py:TestOrchestratorPM.test_generate_tracks_malformed_json, tests/test_orchestrator_pm.py:TestOrchestratorPM.test_generate_tracks_markdown_wrapped, tests/test_orchestrator_pm.py:TestOrchestratorPM.test_generate_tracks_success, tests/test_orchestrator_pm_history.py:TestOrchestratorPMHistory.test_generate_tracks_with_history] + """ # 1. Build Repository Map (Summary View) repo_map = summarize.build_summary_markdown(file_items) # 2. Construct Prompt @@ -125,4 +129,3 @@ if __name__ == "__main__": history = get_track_history_summary() tracks = generate_tracks("Implement a basic unit test for the ai_client.py module.", flat, file_items, history_summary=history) print(json.dumps(tracks, indent=2)) - diff --git a/src/outline_tool.py b/src/outline_tool.py index d351290..9927f08 100644 --- a/src/outline_tool.py +++ b/src/outline_tool.py @@ -54,6 +54,9 @@ class CodeOutliner: return None def walk(node: ast.AST, indent: int = 0) -> None: + """ + [C: src/summarize.py:_summarise_python] + """ if isinstance(node, ast.ClassDef): start_line = node.lineno end_line = getattr(node, "end_lineno", start_line) @@ -87,4 +90,3 @@ def get_outline(path: Path, code: str) -> str: return outliner.outline(code) else: return f"Outlining not supported for {suffix} files yet." - diff --git a/src/patch_modal.py b/src/patch_modal.py index 1e5d722..8461449 100644 --- a/src/patch_modal.py +++ b/src/patch_modal.py @@ -16,6 +16,9 @@ class PatchModalManager: self._on_reject_callback: Optional[Callable[[], None]] = None def request_patch_approval(self, patch_text: str, file_paths: List[str], generated_by: str = "Tier 4 QA") -> bool: + """ + [C: tests/test_patch_modal.py:test_close_modal, tests/test_patch_modal.py:test_reject_patch, tests/test_patch_modal.py:test_request_patch_approval, tests/test_patch_modal.py:test_reset] + """ from time import time self._pending_patch = PendingPatch( patch_text=patch_text, @@ -27,32 +30,56 @@ class PatchModalManager: return True def get_pending_patch(self) -> Optional[PendingPatch]: + """ + [C: tests/test_patch_modal.py:test_patch_modal_manager_init, tests/test_patch_modal.py:test_reject_patch, tests/test_patch_modal.py:test_request_patch_approval, tests/test_patch_modal.py:test_reset] + """ return self._pending_patch def is_modal_shown(self) -> bool: + """ + [C: tests/test_patch_modal.py:test_close_modal, tests/test_patch_modal.py:test_patch_modal_manager_init, tests/test_patch_modal.py:test_reject_patch, tests/test_patch_modal.py:test_request_patch_approval, tests/test_patch_modal.py:test_reset] + """ return self._show_modal def set_apply_callback(self, callback: Callable[[str], bool]) -> None: + """ + [C: tests/test_patch_modal.py:test_apply_callback, tests/test_patch_modal.py:test_reset] + """ self._on_apply_callback = callback def set_reject_callback(self, callback: Callable[[], None]) -> None: + """ + [C: tests/test_patch_modal.py:test_reject_callback, tests/test_patch_modal.py:test_reset] + """ self._on_reject_callback = callback def apply_patch(self, patch_text: str) -> bool: + """ + [C: tests/test_patch_modal.py:test_apply_callback] + """ if self._on_apply_callback: return self._on_apply_callback(patch_text) return False def reject_patch(self) -> None: + """ + [C: tests/test_patch_modal.py:test_reject_callback, tests/test_patch_modal.py:test_reject_patch] + """ self._pending_patch = None self._show_modal = False if self._on_reject_callback: self._on_reject_callback() def close_modal(self) -> None: + """ + [C: tests/test_patch_modal.py:test_close_modal] + """ self._show_modal = False def reset(self) -> None: + """ + [C: tests/test_patch_modal.py:test_reset] + """ self._pending_patch = None self._show_modal = False self._on_apply_callback = None @@ -61,13 +88,19 @@ class PatchModalManager: _patch_modal_manager: Optional[PatchModalManager] = None def get_patch_modal_manager() -> PatchModalManager: + """ + [C: tests/test_patch_modal.py:test_get_patch_modal_manager_singleton] + """ global _patch_modal_manager if _patch_modal_manager is None: _patch_modal_manager = PatchModalManager() return _patch_modal_manager def reset_patch_modal_manager() -> None: + """ + [C: tests/test_patch_modal.py:test_get_patch_modal_manager_singleton] + """ global _patch_modal_manager if _patch_modal_manager: _patch_modal_manager.reset() - _patch_modal_manager = None + _patch_modal_manager = None \ No newline at end of file diff --git a/src/paths.py b/src/paths.py index 0f81e11..0b597ad 100644 --- a/src/paths.py +++ b/src/paths.py @@ -48,35 +48,62 @@ from typing import Optional, Any _RESOLVED: dict[str, Path] = {} def get_config_path() -> Path: + """ + [C: tests/test_paths.py:test_default_paths] + """ root_dir = Path(__file__).resolve().parent.parent return Path(os.environ.get("SLOP_CONFIG", root_dir / "config.toml")) def get_global_presets_path() -> Path: + """ + [C: src/presets.py:PresetManager.__init__, src/presets.py:PresetManager.delete_preset, src/presets.py:PresetManager.get_preset_scope] + """ root_dir = Path(__file__).resolve().parent.parent return Path(os.environ.get("SLOP_GLOBAL_PRESETS", root_dir / "presets.toml")) def get_project_presets_path(project_root: Path) -> Path: + """ + [C: src/presets.py:PresetManager.delete_preset, src/presets.py:PresetManager.get_preset_scope, src/presets.py:PresetManager.project_path] + """ return project_root / "project_presets.toml" def get_global_tool_presets_path() -> Path: + """ + [C: src/tool_presets.py:ToolPresetManager._get_path, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets] + """ root_dir = Path(__file__).resolve().parent.parent return Path(os.environ.get("SLOP_GLOBAL_TOOL_PRESETS", root_dir / "tool_presets.toml")) def get_project_tool_presets_path(project_root: Path) -> Path: + """ + [C: src/tool_presets.py:ToolPresetManager._get_path, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets] + """ return project_root / "project_tool_presets.toml" def get_global_personas_path() -> Path: + """ + [C: src/personas.py:PersonaManager._get_path, src/personas.py:PersonaManager.get_persona_scope, src/personas.py:PersonaManager.load_all] + """ root_dir = Path(__file__).resolve().parent.parent return Path(os.environ.get("SLOP_GLOBAL_PERSONAS", root_dir / "personas.toml")) def get_project_personas_path(project_root: Path) -> Path: + """ + [C: src/personas.py:PersonaManager._get_path, src/personas.py:PersonaManager.get_persona_scope, src/personas.py:PersonaManager.load_all] + """ return project_root / "project_personas.toml" def get_global_workspace_profiles_path() -> Path: + """ + [C: src/workspace_manager.py:WorkspaceManager._get_path, src/workspace_manager.py:WorkspaceManager.load_all_profiles] + """ root_dir = Path(__file__).resolve().parent.parent return Path(os.environ.get("SLOP_GLOBAL_WORKSPACE_PROFILES", root_dir / "workspace_profiles.toml")) def get_project_workspace_profiles_path(project_root: Path) -> Path: + """ + [C: src/workspace_manager.py:WorkspaceManager._get_path, src/workspace_manager.py:WorkspaceManager.load_all_profiles] + """ return project_root / ".ai" / "workspace_profiles.toml" def _resolve_path(env_var: str, config_key: str, default: str) -> Path: @@ -115,6 +142,9 @@ def _get_project_conductor_dir_from_toml(project_root: Path) -> Optional[Path]: return None def get_conductor_dir(project_path: Optional[str] = None) -> Path: + """ + [C: tests/test_paths.py:test_conductor_dir_project_relative, tests/test_project_paths.py:test_get_conductor_dir_default, tests/test_project_paths.py:test_get_conductor_dir_project_specific_with_toml] + """ if not project_path: # Fallback for legacy/tests, but we should avoid this return Path('conductor').resolve() @@ -126,19 +156,31 @@ def get_conductor_dir(project_path: Optional[str] = None) -> Path: return (project_root / "conductor").resolve() def get_logs_dir() -> Path: + """ + [C: src/session_logger.py:close_session, src/session_logger.py:open_session, tests/test_paths.py:test_config_overrides, tests/test_paths.py:test_default_paths, tests/test_paths.py:test_env_var_overrides, tests/test_paths.py:test_precedence] + """ if "logs_dir" not in _RESOLVED: _RESOLVED["logs_dir"] = _resolve_path("SLOP_LOGS_DIR", "logs_dir", "logs/sessions") return _RESOLVED["logs_dir"] def get_scripts_dir() -> Path: + """ + [C: src/session_logger.py:log_tool_call, src/session_logger.py:open_session, tests/test_paths.py:test_config_overrides, tests/test_paths.py:test_default_paths] + """ if "scripts_dir" not in _RESOLVED: _RESOLVED["scripts_dir"] = _resolve_path("SLOP_SCRIPTS_DIR", "scripts_dir", "scripts/generated") return _RESOLVED["scripts_dir"] def get_tracks_dir(project_path: Optional[str] = None) -> Path: + """ + [C: src/project_manager.py:get_all_tracks, tests/test_paths.py:test_conductor_dir_project_relative] + """ return get_conductor_dir(project_path) / "tracks" def get_track_state_dir(track_id: str, project_path: Optional[str] = None) -> Path: + """ + [C: src/project_manager.py:load_track_state, src/project_manager.py:save_track_state, tests/test_paths.py:test_conductor_dir_project_relative] + """ return get_tracks_dir(project_path) / track_id def get_archive_dir(project_path: Optional[str] = None) -> Path: @@ -167,5 +209,8 @@ def get_full_path_info() -> dict[str, dict[str, Any]]: } def reset_resolved() -> None: - """For testing only - clear cached resolutions.""" - _RESOLVED.clear() + """ + For testing only - clear cached resolutions. + [C: tests/conftest.py:reset_paths, tests/test_app_controller_offloading.py:tmp_session_dir, tests/test_gui_phase3.py:test_conductor_setup_scan, tests/test_paths.py:reset_paths, tests/test_project_paths.py:test_get_all_tracks_project_specific, tests/test_project_paths.py:test_get_conductor_dir_default, tests/test_project_paths.py:test_get_conductor_dir_project_specific_with_toml] + """ + _RESOLVED.clear() \ No newline at end of file diff --git a/src/performance_monitor.py b/src/performance_monitor.py index 325fd4a..9de6a3a 100644 --- a/src/performance_monitor.py +++ b/src/performance_monitor.py @@ -75,6 +75,9 @@ class PerformanceScope: def get_monitor() -> PerformanceMonitor: + """ + [C: tests/test_perf_aggregate.py:test_build_tier3_context_scaling, tests/test_perf_dag.py:test_dag_performance] + """ global _instance if _instance is None: _instance = PerformanceMonitor() @@ -82,9 +85,10 @@ def get_monitor() -> PerformanceMonitor: class PerformanceMonitor: """ - Tracks application performance metrics like FPS, frame time, and CPU usage. - Supports thread-safe tracking for individual components with efficient moving averages. - """ + + Tracks application performance metrics like FPS, frame time, and CPU usage. + Supports thread-safe tracking for individual components with efficient moving averages. + """ def __init__(self, history_size: int = 300) -> None: self.enabled: bool = False self.history_size = history_size @@ -154,6 +158,9 @@ class PerformanceMonitor: return self._history_sums[key] / len(h) def start_frame(self) -> None: + """ + [C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing] + """ now = time.perf_counter() with self._lock: if self._last_frame_start_time > 0: @@ -165,6 +172,9 @@ class PerformanceMonitor: self._frame_count += 1 def end_frame(self) -> None: + """ + [C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing] + """ if self._start_time is None: return now = time.perf_counter() @@ -193,12 +203,18 @@ class PerformanceMonitor: self._fps_timer = 0.0 def start_component(self, name: str) -> None: + """ + [C: tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics] + """ if not self.enabled: return now = time.perf_counter() with self._lock: self._component_starts[name] = now def end_component(self, name: str) -> None: + """ + [C: tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics] + """ if not self.enabled: return now = time.perf_counter() with self._lock: @@ -215,7 +231,10 @@ class PerformanceMonitor: self._add_to_history(f'comp_{name}', elapsed) def get_metrics(self) -> dict[str, float]: - """Returns current metrics and their moving averages. Thread-safe.""" + """ + Returns current metrics and their moving averages. Thread-safe. + [C: tests/test_perf_aggregate.py:test_build_tier3_context_scaling, tests/test_perf_dag.py:test_dag_performance, tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager] + """ with self._lock: fps = self._fps last_ft = self._last_frame_time @@ -246,7 +265,10 @@ class PerformanceMonitor: return metrics def get_history(self, key: str) -> List[float]: - """Returns a snapshot of the full history buffer for a specific metric key.""" + """ + Returns a snapshot of the full history buffer for a specific metric key. + [C: tests/test_history.py:test_initial_state, tests/test_history.py:test_push_state] + """ with self._lock: if key in self._history: return list(self._history[key]) @@ -255,11 +277,16 @@ class PerformanceMonitor: return [] def scope(self, name: str) -> PerformanceScope: - """Returns a context manager for timing a component.""" + """ + Returns a context manager for timing a component. + [C: tests/test_perf_aggregate.py:test_build_tier3_context_scaling, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager] + """ return PerformanceScope(self, name) def stop(self) -> None: + """ + [C: tests/test_performance_monitor.py:test_perf_monitor_basic_timing, tests/test_performance_monitor.py:test_perf_monitor_component_timing, tests/test_performance_monitor.py:test_perf_monitor_extended_metrics, tests/test_performance_monitor.py:test_perf_monitor_scope_context_manager, tests/test_websocket_server.py:test_websocket_subscription_and_broadcast] + """ self._stop_event.set() if self._cpu_thread.is_alive(): self._cpu_thread.join(timeout=2.0) - diff --git a/src/personas.py b/src/personas.py index a522243..0cc2387 100644 --- a/src/personas.py +++ b/src/personas.py @@ -12,6 +12,9 @@ class PersonaManager: self.project_root = project_root def _get_path(self, scope: str) -> Path: + """ + [C: src/tool_presets.py:ToolPresetManager.delete_bias_profile, src/tool_presets.py:ToolPresetManager.delete_preset, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.save_profile] + """ if scope == "global": return paths.get_global_personas_path() elif scope == "project": @@ -22,7 +25,10 @@ class PersonaManager: raise ValueError("Invalid scope, must be 'global' or 'project'") def load_all(self) -> Dict[str, Persona]: - """Merges global and project personas into a single dictionary.""" + """ + Merges global and project personas into a single dictionary. + [C: tests/test_persona_manager.py:test_delete_persona, tests/test_persona_manager.py:test_load_all_merged, tests/test_persona_manager.py:test_save_persona, tests/test_preset_manager.py:test_delete_preset, tests/test_preset_manager.py:test_load_all_merged, tests/test_preset_manager.py:test_save_preset_global, tests/test_preset_manager.py:test_save_preset_project, tests/test_presets.py:TestPresetManager.test_delete_preset, tests/test_presets.py:TestPresetManager.test_project_overwrites_global, tests/test_presets.py:TestPresetManager.test_save_and_load_global, tests/test_presets.py:TestPresetManager.test_save_and_load_project] + """ personas = {} global_path = paths.get_global_personas_path() @@ -39,6 +45,9 @@ class PersonaManager: return personas def save_persona(self, persona: Persona, scope: str = "project") -> None: + """ + [C: tests/test_persona_manager.py:test_save_persona] + """ path = self._get_path(scope) data = self._load_file(path) if "personas" not in data: @@ -63,6 +72,9 @@ class PersonaManager: return "project" def delete_persona(self, name: str, scope: str = "project") -> None: + """ + [C: tests/test_persona_manager.py:test_delete_persona] + """ path = self._get_path(scope) data = self._load_file(path) if "personas" in data and name in data["personas"]: @@ -70,6 +82,9 @@ class PersonaManager: self._save_file(path, data) def _load_file(self, path: Path) -> Dict[str, Any]: + """ + [C: src/presets.py:PresetManager.delete_preset, src/presets.py:PresetManager.get_preset_scope, src/presets.py:PresetManager.load_all, src/presets.py:PresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.load_all_profiles, src/workspace_manager.py:WorkspaceManager.save_profile] + """ if not path.exists(): return {} try: @@ -79,7 +94,9 @@ class PersonaManager: return {} def _save_file(self, path: Path, data: Dict[str, Any]) -> None: + """ + [C: src/presets.py:PresetManager.delete_preset, src/presets.py:PresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.save_profile] + """ path.parent.mkdir(parents=True, exist_ok=True) with open(path, "wb") as f: tomli_w.dump(data, f) - diff --git a/src/presets.py b/src/presets.py index 5d8ad9d..ecb3b23 100644 --- a/src/presets.py +++ b/src/presets.py @@ -18,7 +18,10 @@ class PresetManager: return get_project_presets_path(self.project_root) if self.project_root else None def load_all(self) -> Dict[str, Preset]: - """Merges global and project presets into a single dictionary.""" + """ + Merges global and project presets into a single dictionary. + [C: tests/test_persona_manager.py:test_delete_persona, tests/test_persona_manager.py:test_load_all_merged, tests/test_persona_manager.py:test_save_persona, tests/test_preset_manager.py:test_delete_preset, tests/test_preset_manager.py:test_load_all_merged, tests/test_preset_manager.py:test_save_preset_global, tests/test_preset_manager.py:test_save_preset_project, tests/test_presets.py:TestPresetManager.test_delete_preset, tests/test_presets.py:TestPresetManager.test_project_overwrites_global, tests/test_presets.py:TestPresetManager.test_save_and_load_global, tests/test_presets.py:TestPresetManager.test_save_and_load_project] + """ presets: Dict[str, Preset] = {} # Load global presets @@ -41,7 +44,10 @@ class PresetManager: return presets def save_preset(self, preset: Preset, scope: str = "project") -> None: - """Saves a preset to either the global or project-specific TOML file.""" + """ + Saves a preset to either the global or project-specific TOML file. + [C: tests/test_preset_manager.py:test_save_preset_global, tests/test_preset_manager.py:test_save_preset_project, tests/test_preset_manager.py:test_save_preset_project_no_root, tests/test_presets.py:TestPresetManager.test_delete_preset, tests/test_presets.py:TestPresetManager.test_project_overwrites_global, tests/test_presets.py:TestPresetManager.test_save_and_load_global, tests/test_presets.py:TestPresetManager.test_save_and_load_project] + """ path = self.global_path if scope == "global" else self.project_path if not path: if scope == "project": @@ -56,6 +62,9 @@ class PresetManager: self._save_file(path, data) def delete_preset(self, name: str, scope: str) -> None: + """ + [C: tests/test_preset_manager.py:test_delete_preset, tests/test_presets.py:TestPresetManager.test_delete_preset] + """ if scope == "project" and self.project_root: path = get_project_presets_path(self.project_root) else: @@ -82,6 +91,9 @@ class PresetManager: return "project" def _load_file(self, path: Path) -> Dict[str, Any]: + """ + [C: src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.load_all_profiles, src/workspace_manager.py:WorkspaceManager.save_profile] + """ if not path.exists(): return {"presets": {}} try: @@ -97,8 +109,11 @@ class PresetManager: return {"presets": {}} def _save_file(self, path: Path, data: Dict[str, Any]) -> None: + """ + [C: src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.save_profile] + """ if path.parent.exists() and path.parent.is_file(): raise ValueError(f"Cannot save to {path}: Parent directory {path.parent} is a file.") path.parent.mkdir(parents=True, exist_ok=True) with open(path, "wb") as f: - f.write(tomli_w.dumps(data).encode("utf-8")) + f.write(tomli_w.dumps(data).encode("utf-8")) \ No newline at end of file diff --git a/src/project_manager.py b/src/project_manager.py index 6b39b2e..0d506af 100644 --- a/src/project_manager.py +++ b/src/project_manager.py @@ -29,7 +29,10 @@ def parse_ts(s: str) -> Optional[datetime.datetime]: # ── entry serialisation ────────────────────────────────────────────────────── def entry_to_str(entry: dict[str, Any]) -> str: - """Serialise a disc entry dict -> stored string.""" + """ + Serialise a disc entry dict -> stored string. + [C: tests/test_thinking_persistence.py:test_entry_to_str_with_thinking] + """ ts = entry.get("ts", "") role = entry.get("role", "User") content = entry.get("content", "") @@ -46,7 +49,10 @@ def entry_to_str(entry: dict[str, Any]) -> str: return f"{role}:\n{content}" def str_to_entry(raw: str, roles: list[str]) -> dict[str, Any]: - """Parse a stored string back to a disc entry dict.""" + """ + Parse a stored string back to a disc entry dict. + [C: tests/test_thinking_persistence.py:test_str_to_entry_with_thinking] + """ ts = "" rest = raw if rest.startswith("@"): @@ -93,9 +99,15 @@ def get_git_log(git_dir: str, n: int = 5) -> str: # ── default structures ─────────────────────────────────────────────────────── def default_discussion() -> dict[str, Any]: + """ + [C: tests/test_discussion_takes.py:TestDiscussionTakes.test_promote_take_renames_discussion] + """ return {"git_commit": "", "last_updated": now_ts(), "history": []} def default_project(name: str = "unnamed") -> dict[str, Any]: + """ + [C: tests/test_deepseek_infra.py:test_default_project_includes_reasoning_role, tests/test_discussion_takes.py:TestDiscussionTakes.setUp, tests/test_history_management.py:test_history_persistence_across_turns, tests/test_history_management.py:test_save_separation, tests/test_project_manager_modes.py:test_default_project_execution_mode, tests/test_project_manager_modes.py:test_load_save_execution_mode, tests/test_project_serialization.py:TestProjectSerialization.test_default_roles_include_context, tests/test_project_serialization.py:TestProjectSerialization.test_fileitem_roundtrip] + """ return { "project": {"name": name, "git_dir": "", "system_prompt": "", "main_context": "", "execution_mode": "native"}, "output": {"output_dir": "./md_gen"}, @@ -148,15 +160,20 @@ def default_project(name: str = "unnamed") -> dict[str, Any]: # ── load / save ────────────────────────────────────────────────────────────── def get_history_path(project_path: Union[str, Path]) -> Path: - """Return the Path to the sibling history TOML file for a given project.""" + """ + Return the Path to the sibling history TOML file for a given project. + [C: tests/test_history_management.py:test_save_separation] + """ p = Path(project_path) return p.parent / f"{p.stem}_history.toml" def load_project(path: Union[str, Path]) -> dict[str, Any]: """ - Load a project TOML file. - Automatically migrates legacy 'discussion' keys to a sibling history file. - """ + + Load a project TOML file. + Automatically migrates legacy 'discussion' keys to a sibling history file. + [C: tests/test_history_management.py:test_history_persistence_across_turns, tests/test_history_management.py:test_migration_on_load, tests/test_project_manager_modes.py:test_load_save_execution_mode, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_project_serialization.py:TestProjectSerialization.test_fileitem_roundtrip] + """ with open(path, "rb") as f: proj = tomllib.load(f) # Deserialise FileItems in files.paths @@ -176,7 +193,10 @@ def load_project(path: Union[str, Path]) -> dict[str, Any]: return proj def load_history(project_path: Union[str, Path]) -> dict[str, Any]: - """Load the segregated discussion history from its dedicated TOML file.""" + """ + Load the segregated discussion history from its dedicated TOML file. + [C: tests/test_thinking_persistence.py:test_save_and_load_history_with_thinking_segments] + """ hist_path = get_history_path(project_path) if hist_path.exists(): with open(hist_path, "rb") as f: @@ -184,7 +204,10 @@ def load_history(project_path: Union[str, Path]) -> dict[str, Any]: return {} def clean_nones(data: Any) -> Any: - """Recursively remove None values from a dictionary/list.""" + """ + Recursively remove None values from a dictionary/list. + [C: tests/test_thinking_persistence.py:test_clean_nones_removes_thinking] + """ if isinstance(data, dict): return {k: clean_nones(v) for k, v in data.items() if v is not None} elif isinstance(data, list): @@ -193,9 +216,11 @@ def clean_nones(data: Any) -> Any: def save_project(proj: dict[str, Any], path: Union[str, Path], disc_data: Optional[dict[str, Any]] = None) -> None: """ - Save the project TOML. - If 'discussion' is present in proj, it is moved to the sibling history file. - """ + + Save the project TOML. + If 'discussion' is present in proj, it is moved to the sibling history file. + [C: tests/test_history_management.py:test_history_persistence_across_turns, tests/test_history_management.py:test_save_separation, tests/test_project_manager_modes.py:test_load_save_execution_mode, tests/test_project_serialization.py:TestProjectSerialization.test_fileitem_roundtrip, tests/test_thinking_persistence.py:test_save_and_load_history_with_thinking_segments] + """ proj = clean_nones(proj) # Serialise FileItems if "files" in proj and "paths" in proj["files"]: @@ -253,7 +278,10 @@ def flat_config(proj: dict[str, Any], disc_name: Optional[str] = None, track_id: # ── context presets ────────────────────────────────────────────────────────── def save_context_preset(project_dict: dict, preset_name: str, files: list[str], screenshots: list[str]) -> None: - """Save a named context preset (files + screenshots) into the project dict.""" + """ + Save a named context preset (files + screenshots) into the project dict. + [C: tests/test_context_presets.py:test_save_context_preset] + """ if "context_presets" not in project_dict: project_dict["context_presets"] = {} project_dict["context_presets"][preset_name] = { @@ -262,21 +290,29 @@ def save_context_preset(project_dict: dict, preset_name: str, files: list[str], } def load_context_preset(project_dict: dict, preset_name: str) -> dict: - """Return the files and screenshots for a named preset.""" + """ + Return the files and screenshots for a named preset. + [C: tests/test_context_presets.py:test_load_context_preset, tests/test_context_presets.py:test_load_nonexistent_preset] + """ if "context_presets" not in project_dict or preset_name not in project_dict["context_presets"]: raise KeyError(f"Preset '{preset_name}' not found in project context_presets.") return project_dict["context_presets"][preset_name] def delete_context_preset(project_dict: dict, preset_name: str) -> None: - """Remove a named preset if it exists.""" + """ + Remove a named preset if it exists. + [C: tests/test_context_presets.py:test_delete_context_preset, tests/test_context_presets.py:test_delete_nonexistent_preset_no_error] + """ if "context_presets" in project_dict: project_dict["context_presets"].pop(preset_name, None) # ── track state persistence ───────────────────────────────────────────────── def save_track_state(track_id: str, state: 'TrackState', base_dir: Union[str, Path] = ".") -> None: """ - Saves a TrackState object to conductor/tracks//state.toml. - """ + + Saves a TrackState object to conductor/tracks//state.toml. + [C: tests/test_project_manager_tracks.py:test_get_all_tracks_with_state, tests/test_track_state_persistence.py:test_track_state_persistence] + """ track_dir = paths.get_track_state_dir(track_id, project_path=str(base_dir)) track_dir.mkdir(parents=True, exist_ok=True) state_file = track_dir / "state.toml" @@ -286,8 +322,10 @@ def save_track_state(track_id: str, state: 'TrackState', base_dir: Union[str, Pa def load_track_state(track_id: str, base_dir: Union[str, Path] = ".") -> Optional['TrackState']: """ - Loads a TrackState object from conductor/tracks//state.toml. - """ + + Loads a TrackState object from conductor/tracks//state.toml. + [C: tests/test_track_state_persistence.py:test_track_state_persistence] + """ from src.models import TrackState state_file = paths.get_track_state_dir(track_id, project_path=str(base_dir)) / 'state.toml' if not state_file.exists(): @@ -298,9 +336,10 @@ def load_track_state(track_id: str, base_dir: Union[str, Path] = ".") -> Optiona def load_track_history(track_id: str, base_dir: Union[str, Path] = ".") -> list[str]: """ - Loads the discussion history for a specific track from its state.toml. - Returns a list of entry strings formatted with @timestamp. - """ + + Loads the discussion history for a specific track from its state.toml. + Returns a list of entry strings formatted with @timestamp. + """ state = load_track_state(track_id, base_dir) if not state: return [] @@ -315,9 +354,10 @@ def load_track_history(track_id: str, base_dir: Union[str, Path] = ".") -> list[ def save_track_history(track_id: str, history: list[str], base_dir: Union[str, Path] = ".") -> None: """ - Saves the discussion history for a specific track to its state.toml. - 'history' is expected to be a list of formatted strings. - """ + + Saves the discussion history for a specific track to its state.toml. + 'history' is expected to be a list of formatted strings. + """ state = load_track_state(track_id, base_dir) if not state: return @@ -328,12 +368,14 @@ def save_track_history(track_id: str, history: list[str], base_dir: Union[str, P def get_all_tracks(base_dir: Union[str, Path] = ".") -> list[dict[str, Any]]: """ - Scans the conductor/tracks/ directory and returns a list of dictionaries - containing track metadata: 'id', 'title', 'status', 'complete', 'total', - and 'progress' (0.0 to 1.0). - Handles missing or malformed metadata.json or state.toml by falling back - to available info or defaults. - """ + + Scans the conductor/tracks/ directory and returns a list of dictionaries + containing track metadata: 'id', 'title', 'status', 'complete', 'total', + and 'progress' (0.0 to 1.0). + Handles missing or malformed metadata.json or state.toml by falling back + to available info or defaults. + [C: tests/test_project_manager_tracks.py:test_get_all_tracks_empty, tests/test_project_manager_tracks.py:test_get_all_tracks_malformed, tests/test_project_manager_tracks.py:test_get_all_tracks_with_metadata_json, tests/test_project_manager_tracks.py:test_get_all_tracks_with_state, tests/test_project_paths.py:test_get_all_tracks_project_specific] + """ tracks_dir = paths.get_tracks_dir(project_path=str(base_dir)) if not tracks_dir.exists(): return [] @@ -394,8 +436,10 @@ def get_all_tracks(base_dir: Union[str, Path] = ".") -> list[dict[str, Any]]: def calculate_track_progress(tickets: list) -> dict: """ - Calculates track progress based on ticket statuses. - percentage (float), completed (int), total (int), in_progress (int), blocked (int), todo (int) + + Calculates track progress based on ticket statuses. + percentage (float), completed (int), total (int), in_progress (int), blocked (int), todo (int) + [C: tests/test_progress_viz.py:test_calculate_track_progress_all_completed, tests/test_progress_viz.py:test_calculate_track_progress_all_todo, tests/test_progress_viz.py:test_calculate_track_progress_empty, tests/test_progress_viz.py:test_calculate_track_progress_mixed] """ total = len(tickets) if total == 0: @@ -427,8 +471,10 @@ def calculate_track_progress(tickets: list) -> dict: def branch_discussion(project_dict: dict, source_id: str, new_id: str, message_index: int) -> None: """ - Creates a new discussion in project_dict['discussion']['discussions'] by copying - the history from source_id up to (and including) message_index, and sets active to new_id. + + Creates a new discussion in project_dict['discussion']['discussions'] by copying + the history from source_id up to (and including) message_index, and sets active to new_id. + [C: tests/test_discussion_takes.py:TestDiscussionTakes.test_branch_discussion_creates_new_take] """ if "discussion" not in project_dict or "discussions" not in project_dict["discussion"]: return @@ -445,7 +491,10 @@ def branch_discussion(project_dict: dict, source_id: str, new_id: str, message_i project_dict["discussion"]["active"] = new_id def promote_take(project_dict: dict, take_id: str, new_id: str) -> None: - """Renames a take_id to new_id in the discussions dict.""" + """ + Renames a take_id to new_id in the discussions dict. + [C: tests/test_discussion_takes.py:TestDiscussionTakes.test_promote_take_renames_discussion] + """ if "discussion" not in project_dict or "discussions" not in project_dict["discussion"]: return if take_id not in project_dict["discussion"]["discussions"]: @@ -456,4 +505,4 @@ def promote_take(project_dict: dict, take_id: str, new_id: str) -> None: # If the take was active, update the active pointer if project_dict["discussion"].get("active") == take_id: - project_dict["discussion"]["active"] = new_id + project_dict["discussion"]["active"] = new_id \ No newline at end of file diff --git a/src/rag_engine.py b/src/rag_engine.py index 699bcca..90da659 100644 --- a/src/rag_engine.py +++ b/src/rag_engine.py @@ -94,6 +94,9 @@ class RAGEngine: return self.collection.count() == 0 def add_documents(self, ids: List[str], texts: List[str], metadatas: Optional[List[Dict[str, Any]]] = None): + """ + [C: tests/test_rag_engine.py:test_rag_engine_chroma] + """ if not self.config.enabled or self.collection == "mock": return embeddings = self.embedding_provider.embed(texts) @@ -202,6 +205,9 @@ class RAGEngine: return asyncio.run(_async_search_mcp()) def search(self, query: str, top_k: int = 5) -> List[Dict[str, Any]]: + """ + [C: tests/mock_concurrent_mma.py:main, tests/test_rag_engine.py:test_rag_engine_chroma] + """ if not self.config.enabled: return [] if self.config.vector_store.provider == 'mcp': @@ -227,6 +233,9 @@ class RAGEngine: return ret def delete_documents(self, ids: List[str]): + """ + [C: tests/test_rag_engine.py:test_rag_engine_chroma] + """ if not self.config.enabled or self.collection == "mock": return self.collection.delete(ids=ids) @@ -243,4 +252,4 @@ class RAGEngine: if not self.config.enabled or self.collection == "mock": return for path in file_paths: - self.collection.delete(where={"path": path}) + self.collection.delete(where={"path": path}) \ No newline at end of file diff --git a/src/session_logger.py b/src/session_logger.py index ed2d478..7aeeb7d 100644 --- a/src/session_logger.py +++ b/src/session_logger.py @@ -43,9 +43,11 @@ def _now_ts() -> str: def open_session(label: Optional[str] = None) -> None: """ - Called once at GUI startup. Creates the log directories if needed and - opens the log files for this session within a sub-directory. - """ + + Called once at GUI startup. Creates the log directories if needed and + opens the log files for this session within a sub-directory. + [C: tests/test_app_controller_offloading.py:tmp_session_dir, tests/test_logging_e2e.py:test_logging_e2e, tests/test_session_logger_optimization.py:test_log_tool_call_saves_in_session_scripts, tests/test_session_logger_optimization.py:test_log_tool_output_saves_in_session_outputs, tests/test_session_logger_optimization.py:test_session_directory_and_subdirectories_creation, tests/test_session_logger_reset.py:test_reset_session, tests/test_session_logging.py:test_open_session_creates_subdir_and_registry] + """ global _ts, _session_id, _session_dir, _comms_fh, _tool_fh, _api_fh, _cli_fh, _seq, _output_seq if _comms_fh is not None: return @@ -86,7 +88,10 @@ def open_session(label: Optional[str] = None) -> None: atexit.register(close_session) def close_session() -> None: - """Flush and close all log files. Called on clean exit.""" + """ + Flush and close all log files. Called on clean exit. + [C: tests/test_app_controller_offloading.py:tmp_session_dir, tests/test_logging_e2e.py:e2e_setup, tests/test_logging_e2e.py:test_logging_e2e, tests/test_session_logger_optimization.py:temp_session_setup, tests/test_session_logger_reset.py:temp_logs, tests/test_session_logging.py:temp_logs] + """ global _comms_fh, _tool_fh, _api_fh, _cli_fh, _session_id if _comms_fh is None: return @@ -130,9 +135,11 @@ def log_api_hook(method: str, path: str, payload: str) -> None: def log_comms(entry: dict[str, Any]) -> None: """ - Append one comms entry to the comms log file as a JSON-L line. - Thread-safe (GIL + line-buffered file). - """ + + Append one comms entry to the comms log file as a JSON-L line. + Thread-safe (GIL + line-buffered file). + [C: tests/test_logging_e2e.py:test_logging_e2e] + """ if _comms_fh is None: return try: @@ -142,9 +149,11 @@ def log_comms(entry: dict[str, Any]) -> None: def log_tool_call(script: str, result: str, script_path: Optional[str]) -> Optional[str]: """ - Append a tool-call record to the toolcalls log and write the PS1 script to - the session's scripts directory. Returns the path of the written script file. - """ + + Append a tool-call record to the toolcalls log and write the PS1 script to + the session's scripts directory. Returns the path of the written script file. + [C: tests/test_session_logger_optimization.py:test_log_tool_call_saves_in_session_scripts] + """ global _seq if _tool_fh is None: return script_path @@ -184,9 +193,11 @@ def log_tool_call(script: str, result: str, script_path: Optional[str]) -> Optio def log_tool_output(content: str) -> Optional[str]: """ - Save tool output content to a unique file in the session's outputs directory. - Returns the path of the written file. - """ + + Save tool output content to a unique file in the session's outputs directory. + Returns the path of the written file. + [C: tests/test_session_logger_optimization.py:test_log_tool_output_returns_none_if_no_session, tests/test_session_logger_optimization.py:test_log_tool_output_saves_in_session_outputs] + """ global _output_seq if _session_dir is None: return None @@ -222,4 +233,3 @@ def log_cli_call(command: str, stdin_content: Optional[str], stdout_content: Opt _cli_fh.flush() except Exception: pass - diff --git a/src/shader_manager.py b/src/shader_manager.py index da535d9..622ee43 100644 --- a/src/shader_manager.py +++ b/src/shader_manager.py @@ -7,6 +7,9 @@ class ShaderManager: self.pp_program = None def compile_shader(self, vertex_src: str, fragment_src: str) -> int: + """ + [C: tests/test_shader_manager.py:test_shader_manager_initialization_and_compilation] + """ program = gl.glCreateProgram() def _compile(src, shader_type): @@ -41,6 +44,9 @@ class ShaderManager: return program def update_uniforms(self, uniforms: dict): + """ + [C: tests/test_shader_manager.py:test_shader_manager_uniform_update] + """ if self.program is None: return @@ -62,6 +68,9 @@ class ShaderManager: gl.glUniform4f(loc, value[0], value[1], value[2], value[3]) def setup_background_shader(self): + """ + [C: tests/test_dynamic_background.py:test_dynamic_background_rendering] + """ vertex_src = """ #version 330 core const vec2 positions[4] = vec2[]( @@ -88,6 +97,9 @@ void main() { self.bg_program = self.compile_shader(vertex_src, fragment_src) def render_background(self, width, height, time): + """ + [C: tests/test_dynamic_background.py:test_dynamic_background_rendering] + """ if not self.bg_program: return gl.glUseProgram(self.bg_program) @@ -101,6 +113,9 @@ void main() { gl.glUseProgram(0) def setup_post_process_shader(self): + """ + [C: tests/test_post_process.py:TestPostProcess.test_setup_post_process_shader] + """ vertex_src = """ #version 330 core const vec2 positions[4] = vec2[]( @@ -137,6 +152,9 @@ void main() { self.pp_program = self.compile_shader(vertex_src, fragment_src) def render_post_process(self, texture_id, width, height, time): + """ + [C: tests/test_post_process.py:TestPostProcess.test_render_post_process] + """ if not self.pp_program: return gl.glUseProgram(self.pp_program) @@ -150,4 +168,4 @@ void main() { gl.glUniform1f(u_time_loc, float(time)) gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4) gl.glBindTexture(gl.GL_TEXTURE_2D, 0) - gl.glUseProgram(0) + gl.glUseProgram(0) \ No newline at end of file diff --git a/src/shaders.py b/src/shaders.py index 2be6274..5f3a617 100644 --- a/src/shaders.py +++ b/src/shaders.py @@ -2,8 +2,9 @@ from imgui_bundle import imgui def draw_soft_shadow(draw_list: imgui.ImDrawList, p_min: imgui.ImVec2, p_max: imgui.ImVec2, color: imgui.ImVec4, shadow_size: float = 10.0, rounding: float = 0.0) -> None: """ - Simulates a soft shadow effect by drawing multiple concentric rounded rectangles - with decreasing alpha values. This is a faux-shader effect using primitive batching. + + Simulates a soft shadow effect by drawing multiple concentric rounded rectangles + with decreasing alpha values. This is a faux-shader effect using primitive batching. """ r, g, b, a = color.x, color.y, color.z, color.w steps = int(shadow_size) @@ -38,8 +39,9 @@ def draw_soft_shadow(draw_list: imgui.ImDrawList, p_min: imgui.ImVec2, p_max: im def apply_faux_acrylic_glass(draw_list: imgui.ImDrawList, p_min: imgui.ImVec2, p_max: imgui.ImVec2, base_color: imgui.ImVec4, rounding: float = 0.0) -> None: """ - Simulates a faux acrylic/glass effect by drawing a semi-transparent base, - a gradient overlay for 'shine', and a subtle inner border for 'edge glow'. + + Simulates a faux acrylic/glass effect by drawing a semi-transparent base, + a gradient overlay for 'shine', and a subtle inner border for 'edge glow'. """ r, g, b, a = base_color.x, base_color.y, base_color.z, base_color.w @@ -70,4 +72,3 @@ def apply_faux_acrylic_glass(draw_list: imgui.ImDrawList, p_min: imgui.ImVec2, p flags=imgui.ImDrawFlags_.round_corners_all if rounding > 0 else imgui.ImDrawFlags_.none, thickness=1.0 ) - diff --git a/src/shell_runner.py b/src/shell_runner.py index 6652a00..f91dba3 100644 --- a/src/shell_runner.py +++ b/src/shell_runner.py @@ -53,13 +53,15 @@ def _build_subprocess_env() -> dict[str, str]: def run_powershell(script: str, base_dir: str, qa_callback: Optional[Callable[[str], str]] = None, patch_callback: Optional[Callable[[str, str], Optional[str]]] = None) -> str: """ - Run a PowerShell script with working directory set to base_dir. - Returns a string combining stdout, stderr, and exit code. - Environment is configured via mcp_env.toml (project root). - If qa_callback is provided and the command fails or has stderr, - the callback is called with the stderr content and its result is appended. - If patch_callback is provided, it receives (error, file_context) and returns patch text. - """ + + Run a PowerShell script with working directory set to base_dir. + Returns a string combining stdout, stderr, and exit code. + Environment is configured via mcp_env.toml (project root). + If qa_callback is provided and the command fails or has stderr, + the callback is called with the stderr content and its result is appended. + If patch_callback is provided, it receives (error, file_context) and returns patch text. + [C: tests/test_tier4_interceptor.py:test_run_powershell_no_qa_callback_on_success, tests/test_tier4_interceptor.py:test_run_powershell_optional_qa_callback, tests/test_tier4_interceptor.py:test_run_powershell_qa_callback_on_failure, tests/test_tier4_interceptor.py:test_run_powershell_qa_callback_on_stderr_only] + """ safe_dir: str = str(base_dir).replace("'", "''") full_script: str = f"Set-Location -LiteralPath '{safe_dir}'\n{script}" exe: Optional[str] = next((x for x in ["powershell.exe", "pwsh.exe", "powershell", "pwsh"] if shutil.which(x)), None) @@ -97,4 +99,3 @@ def run_powershell(script: str, base_dir: str, qa_callback: Optional[Callable[[s if 'process' in locals() and process: subprocess.run(["taskkill", "/F", "/T", "/PID", str(process.pid)], capture_output=True) return f"ERROR: {e}" - diff --git a/src/summarize.py b/src/summarize.py index ec98770..f5c3e19 100644 --- a/src/summarize.py +++ b/src/summarize.py @@ -153,8 +153,10 @@ _SUMMARISERS: dict[str, Callable[[Path, str], str]] = { def summarise_file(path: Path, content: str) -> str: """ - Return a compact markdown summary string for a single file. - `content` is the already-read file text (or an error string). + + Return a compact markdown summary string for a single file. + `content` is the already-read file text (or an error string). + [C: tests/test_subagent_summarization.py:test_summarise_file_integration] """ content_hash = get_file_hash(content) cached = _summary_cache.get_summary(str(path), content_hash) @@ -190,9 +192,10 @@ def summarise_file(path: Path, content: str) -> str: def summarise_items(file_items: list[dict[str, Any]]) -> list[dict[str, Any]]: """ - Given a list of file_item dicts (as returned by aggregate.build_file_items), - return a parallel list of dicts with an added `summary` key. - """ + + Given a list of file_item dicts (as returned by aggregate.build_file_items), + return a parallel list of dicts with an added `summary` key. + """ result = [] for item in file_items: path = item.get("path") @@ -208,9 +211,10 @@ def summarise_items(file_items: list[dict[str, Any]]) -> list[dict[str, Any]]: def build_summary_markdown(file_items: list[dict[str, Any]]) -> str: """ - Build a compact markdown string of file summaries, suitable for the - initial block instead of full file contents. - """ + + Build a compact markdown string of file summaries, suitable for the + initial block instead of full file contents. + """ summarised = summarise_items(file_items) parts = [] for item in summarised: @@ -218,4 +222,3 @@ def build_summary_markdown(file_items: list[dict[str, Any]]) -> str: summary = item.get("summary", "") parts.append(f"### `{path}`\n\n{summary}") return "\n\n---\n\n".join(parts) - diff --git a/src/summary_cache.py b/src/summary_cache.py index b3b4dee..06158c6 100644 --- a/src/summary_cache.py +++ b/src/summary_cache.py @@ -4,14 +4,18 @@ from pathlib import Path from typing import Optional, Dict def get_file_hash(content: str) -> str: - """Returns SHA256 hash of the content.""" + """ + Returns SHA256 hash of the content. + [C: tests/test_summary_cache.py:test_get_file_hash, tests/test_summary_cache.py:test_summary_cache] + """ return hashlib.sha256(content.encode("utf-8")).hexdigest() class SummaryCache: """ - A hash-based cache for file summaries to avoid redundant processing. - Invalidates when content hash changes. - """ + + A hash-based cache for file summaries to avoid redundant processing. + Invalidates when content hash changes. + """ def __init__(self, cache_file: Optional[str] = None, max_entries: int = 1000): if cache_file: self.cache_file = Path(cache_file) @@ -23,7 +27,10 @@ class SummaryCache: self.load() def load(self) -> None: - """Loads cache from disk.""" + """ + Loads cache from disk. + [C: src/tool_presets.py:ToolPresetManager._read_raw, src/workspace_manager.py:WorkspaceManager._load_file, tests/test_gui_phase3.py:test_create_track, tests/test_history_management.py:test_save_separation, tests/test_saved_presets_sim.py:test_preset_manager_modal, tests/test_session_logging.py:test_open_session_creates_subdir_and_registry, tests/test_visual_sim_gui_ux.py:test_gui_track_creation] + """ if self.cache_file.exists(): try: with open(self.cache_file, "r", encoding="utf-8") as f: @@ -41,7 +48,10 @@ class SummaryCache: pass def get_summary(self, file_path: str, content_hash: str) -> Optional[str]: - """Returns cached summary if hash matches, otherwise None.""" + """ + Returns cached summary if hash matches, otherwise None. + [C: tests/test_summary_cache.py:test_summary_cache, tests/test_summary_cache.py:test_summary_cache_lru] + """ entry = self.cache.get(file_path) if entry and entry.get("hash") == content_hash: # LRU: move to end @@ -51,7 +61,10 @@ class SummaryCache: return None def set_summary(self, file_path: str, content_hash: str, summary: str) -> None: - """Stores summary in cache and saves to disk.""" + """ + Stores summary in cache and saves to disk. + [C: tests/test_summary_cache.py:test_summary_cache, tests/test_summary_cache.py:test_summary_cache_lru] + """ if file_path in self.cache: self.cache.pop(file_path) self.cache[file_path] = { @@ -66,7 +79,10 @@ class SummaryCache: self.save() def clear(self) -> None: - """Clears the cache both in-memory and on disk.""" + """ + Clears the cache both in-memory and on disk. + [C: tests/conftest.py:reset_ai_client] + """ self.cache.clear() if self.cache_file.exists(): try: @@ -85,4 +101,4 @@ class SummaryCache: return { "entries": len(self.cache), "size_bytes": size_bytes - } + } \ No newline at end of file diff --git a/src/synthesis_formatter.py b/src/synthesis_formatter.py index 34a3578..38bb85f 100644 --- a/src/synthesis_formatter.py +++ b/src/synthesis_formatter.py @@ -1,4 +1,7 @@ def format_takes_diff(takes: dict[str, list[dict]]) -> str: + """ + [C: tests/test_synthesis_formatter.py:test_format_takes_diff_common_prefix, tests/test_synthesis_formatter.py:test_format_takes_diff_empty, tests/test_synthesis_formatter.py:test_format_takes_diff_no_common_prefix, tests/test_synthesis_formatter.py:test_format_takes_diff_single_take] + """ if not takes: return "" @@ -39,4 +42,4 @@ def format_takes_diff(takes: dict[str, list[dict]]) -> str: variations_text = "=== Variations ===\n" + "\n".join(variation_lines) - return shared_text + "\n\n" + variations_text + return shared_text + "\n\n" + variations_text \ No newline at end of file diff --git a/src/theme.py b/src/theme.py index 38fd4a3..965270f 100644 --- a/src/theme.py +++ b/src/theme.py @@ -293,11 +293,17 @@ def get_current_scale() -> float: return _current_scale def get_shader_config(key: str) -> Any: - """Get a specific shader configuration value.""" + """ + Get a specific shader configuration value. + [C: tests/test_shader_config.py:test_shader_config_parsing] + """ return _shader_config.get(key) def get_window_frame_config() -> bool: - """Get the window frame configuration.""" + """ + Get the window frame configuration. + [C: tests/test_shader_config.py:test_shader_config_parsing] + """ return _shader_config.get("custom_window_frame", False) def get_palette_colours(name: str) -> dict[str, Any]: @@ -306,11 +312,13 @@ def get_palette_colours(name: str) -> dict[str, Any]: def apply(palette_name: str, overrides: dict[str, Any] | None = None) -> None: """ - Build a global DPG theme from the named palette plus optional per-colour - overrides, and bind it as the default theme. - - overrides: {colour_key: (R,G,B) or (R,G,B,A)} — merged on top of palette. - """ + + Build a global DPG theme from the named palette plus optional per-colour + overrides, and bind it as the default theme. + + overrides: {colour_key: (R,G,B) or (R,G,B,A)} — merged on top of palette. + [C: src/theme_2.py:apply_current, src/theme_2.py:set_child_transparency, src/theme_2.py:set_transparency, tests/test_theme.py:test_theme_apply_sets_rounding_and_padding] + """ global _current_theme_tag, _current_palette _current_palette = palette_name colours = dict(_PALETTES.get(palette_name, {})) @@ -349,11 +357,12 @@ def apply(palette_name: str, overrides: dict[str, Any] | None = None) -> None: def apply_font(font_path: str, size: float = 14.0) -> None: """ - Load the TTF at font_path at the given point size and bind it globally. - Safe to call multiple times. Uses a single persistent font_registry; only - the font *item* tag is tracked. Passing an empty path or a missing file - resets to the DPG built-in font. - """ + + Load the TTF at font_path at the given point size and bind it globally. + Safe to call multiple times. Uses a single persistent font_registry; only + the font *item* tag is tracked. Passing an empty path or a missing file + resets to the DPG built-in font. + """ global _current_font_tag, _current_font_path, _current_font_size, _font_registry_tag _current_font_path = font_path _current_font_size = size @@ -378,7 +387,10 @@ def apply_font(font_path: str, size: float = 14.0) -> None: dpg.bind_font(font) def set_scale(factor: float) -> None: - """Set the global Dear PyGui font/UI scale factor.""" + """ + Set the global Dear PyGui font/UI scale factor. + [C: src/theme_2.py:apply_current] + """ global _current_scale _current_scale = factor dpg.set_global_font_scale(factor) @@ -392,7 +404,10 @@ def save_to_config(config: dict[str, Any]) -> None: config["theme"]["scale"] = _current_scale def load_from_config(config: dict[str, Any]) -> None: - """Read [theme] from config and apply everything.""" + """ + Read [theme] from config and apply everything. + [C: tests/test_shader_config.py:test_shader_config_parsing] + """ t = config.get("theme", {}) palette = t.get("palette", "DPG Default") font_path = t.get("font_path", "") @@ -407,4 +422,3 @@ def load_from_config(config: dict[str, Any]) -> None: _shader_config["bloom"] = t.get("shader_bloom", False) _shader_config["bg"] = t.get("shader_bg", "none") _shader_config["custom_window_frame"] = t.get("custom_window_frame", False) - diff --git a/src/theme_2.py b/src/theme_2.py index e71f508..920417f 100644 --- a/src/theme_2.py +++ b/src/theme_2.py @@ -18,7 +18,10 @@ import src.theme_nerv # Only keys that differ from the ImGui dark defaults need to be listed. def _c(r: int, g: int, b: int, a: int = 255) -> tuple[float, float, float, float]: - """Convert 0-255 RGBA to 0.0-1.0 floats.""" + """ + Convert 0-255 RGBA to 0.0-1.0 floats. + [C: src/theme_nerv.py:module] + """ return (r / 255.0, g / 255.0, b / 255.0, a / 255.0) _PALETTES: dict[str, dict[int, tuple]] = { @@ -271,8 +274,9 @@ def set_child_transparency(val: float) -> None: def apply(palette_name: str) -> None: """ - Apply a named palette by setting all ImGui style colors and applying global professional styling. - """ + + Apply a named palette by setting all ImGui style colors and applying global professional styling. + """ global _current_palette _current_palette = palette_name if palette_name == 'NERV': @@ -392,4 +396,3 @@ def get_tweaked_theme() -> hello_imgui.ImGuiTweakedTheme: # Sync tweaks tt.tweaks.rounding = 6.0 return tt - diff --git a/src/theme_nerv.py b/src/theme_nerv.py index 436cafb..3faab4b 100644 --- a/src/theme_nerv.py +++ b/src/theme_nerv.py @@ -62,7 +62,10 @@ NERV_PALETTE = { } def apply_nerv() -> None: - """Apply NERV theme with hard edges and specific palette.""" + """ + Apply NERV theme with hard edges and specific palette. + [C: tests/test_theme_nerv.py:test_apply_nerv_sets_rounding_and_colors] + """ style = imgui.get_style() for col_enum, rgba in NERV_PALETTE.items(): style.set_color_(col_enum, imgui.ImVec4(*rgba)) @@ -82,4 +85,3 @@ def apply_nerv() -> None: style.popup_border_size = 1.0 style.child_border_size = 1.0 style.tab_border_size = 1.0 - diff --git a/src/theme_nerv_fx.py b/src/theme_nerv_fx.py index ea89ed2..be67f0f 100644 --- a/src/theme_nerv_fx.py +++ b/src/theme_nerv_fx.py @@ -8,6 +8,9 @@ class CRTFilter: self.enabled = True def render(self, width: float, height: float): + """ + [C: tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render] + """ if not self.enabled: return draw_list = imgui.get_foreground_draw_list() @@ -64,6 +67,9 @@ class CRTFilter: class StatusFlicker: def get_alpha(self) -> float: # Modulate between 0.7 and 1.0 using sin wave + """ + [C: tests/test_theme_nerv_fx.py:TestThemeNervFx.test_status_flicker_get_alpha] + """ return 0.85 + 0.15 * math.sin(time.time() * 20.0) class AlertPulsing: @@ -71,9 +77,15 @@ class AlertPulsing: self.active = False def update(self, status: str): + """ + [C: tests/test_spawn_interception_v2.py:MockDialog.wait, tests/test_theme_nerv_alert.py:test_alert_pulsing_update, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_update] + """ self.active = status.lower().startswith("error") def render(self, width: float, height: float): + """ + [C: tests/test_theme_nerv_alert.py:test_alert_pulsing_render_active, tests/test_theme_nerv_alert.py:test_alert_pulsing_render_inactive, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_alert_pulsing_render, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_disabled, tests/test_theme_nerv_fx.py:TestThemeNervFx.test_crt_filter_render] + """ if not self.active: return draw_list = imgui.get_foreground_draw_list() @@ -83,4 +95,3 @@ class AlertPulsing: alpha = 0.05 + 0.15 * ((math.sin(time.time() * 4.0) + 1.0) / 2.0) color = imgui.get_color_u32((1.0, 0.0, 0.0, alpha)) draw_list.add_rect((0.0, 0.0), (width, height), color, 0.0, 0, 10.0) - diff --git a/src/thinking_parser.py b/src/thinking_parser.py index 77b3517..827d230 100644 --- a/src/thinking_parser.py +++ b/src/thinking_parser.py @@ -4,9 +4,11 @@ from src.models import ThinkingSegment def parse_thinking_trace(text: str) -> Tuple[List[ThinkingSegment], str]: """ - Parses thinking segments from text and returns (segments, response_content). - Support extraction of thinking traces from ..., ..., - and blocks prefixed with Thinking:. + + Parses thinking segments from text and returns (segments, response_content). + Support extraction of thinking traces from ..., ..., + and blocks prefixed with Thinking:. + [C: tests/test_thinking_trace.py:test_parse_empty_response, tests/test_thinking_trace.py:test_parse_multiple_markers, tests/test_thinking_trace.py:test_parse_no_thinking, tests/test_thinking_trace.py:test_parse_text_thinking_prefix, tests/test_thinking_trace.py:test_parse_thinking_with_empty_response, tests/test_thinking_trace.py:test_parse_xml_thinking_tag, tests/test_thinking_trace.py:test_parse_xml_thought_tag] """ segments = [] @@ -50,4 +52,4 @@ def parse_thinking_trace(text: str) -> Tuple[List[ThinkingSegment], str]: colon_segments, final_remaining = extract_colon_blocks(remaining) segments.extend(colon_segments) - return segments, final_remaining.strip() + return segments, final_remaining.strip() \ No newline at end of file diff --git a/src/tool_bias.py b/src/tool_bias.py index 8cb7577..82c261d 100644 --- a/src/tool_bias.py +++ b/src/tool_bias.py @@ -3,6 +3,9 @@ from src.models import Tool, ToolPreset, BiasProfile class ToolBiasEngine: def apply_semantic_nudges(self, tool_definitions: List[Dict[str, Any]], preset: ToolPreset) -> List[Dict[str, Any]]: + """ + [C: tests/test_tool_bias.py:test_apply_semantic_nudges, tests/test_tool_bias.py:test_parameter_bias_nudging] + """ weight_map = { 5: "[HIGH PRIORITY] ", 4: "[PREFERRED] ", @@ -36,6 +39,9 @@ class ToolBiasEngine: return tool_definitions def generate_tooling_strategy(self, preset: ToolPreset, global_bias: BiasProfile) -> str: + """ + [C: tests/test_tool_bias.py:test_generate_tooling_strategy] + """ lines = ["### Tooling Strategy"] preferred = [] @@ -63,4 +69,3 @@ class ToolBiasEngine: lines.append(f"- {cat}: {mult}x") return "\n\n".join(lines) - diff --git a/src/tool_presets.py b/src/tool_presets.py index f61ab1a..155c286 100644 --- a/src/tool_presets.py +++ b/src/tool_presets.py @@ -10,6 +10,9 @@ class ToolPresetManager: self.project_root = Path(project_root) if project_root else None def _get_path(self, scope: str) -> Path: + """ + [C: src/workspace_manager.py:WorkspaceManager.delete_profile, src/workspace_manager.py:WorkspaceManager.save_profile] + """ if scope == "global": return paths.get_global_tool_presets_path() elif scope == "project": @@ -34,6 +37,9 @@ class ToolPresetManager: tomli_w.dump(data, f) def load_all_presets(self) -> Dict[str, ToolPreset]: + """ + [C: tests/test_tool_preset_manager.py:test_load_all_presets_merged] + """ global_path = paths.get_global_tool_presets_path() global_data = self._read_raw(global_path).get("presets", {}) @@ -52,10 +58,16 @@ class ToolPresetManager: return presets def load_all(self) -> Dict[str, ToolPreset]: - """Backward compatibility for load_all().""" + """ + Backward compatibility for load_all(). + [C: tests/test_persona_manager.py:test_delete_persona, tests/test_persona_manager.py:test_load_all_merged, tests/test_persona_manager.py:test_save_persona, tests/test_preset_manager.py:test_delete_preset, tests/test_preset_manager.py:test_load_all_merged, tests/test_preset_manager.py:test_save_preset_global, tests/test_preset_manager.py:test_save_preset_project, tests/test_presets.py:TestPresetManager.test_delete_preset, tests/test_presets.py:TestPresetManager.test_project_overwrites_global, tests/test_presets.py:TestPresetManager.test_save_and_load_global, tests/test_presets.py:TestPresetManager.test_save_and_load_project] + """ return self.load_all_presets() def save_preset(self, preset: ToolPreset, scope: str = "project") -> None: + """ + [C: tests/test_preset_manager.py:test_save_preset_global, tests/test_preset_manager.py:test_save_preset_project, tests/test_preset_manager.py:test_save_preset_project_no_root, tests/test_presets.py:TestPresetManager.test_delete_preset, tests/test_presets.py:TestPresetManager.test_project_overwrites_global, tests/test_presets.py:TestPresetManager.test_save_and_load_global, tests/test_presets.py:TestPresetManager.test_save_and_load_project] + """ path = self._get_path(scope) data = self._read_raw(path) if "presets" not in data: @@ -64,6 +76,9 @@ class ToolPresetManager: self._write_raw(path, data) def delete_preset(self, name: str, scope: str = "project") -> None: + """ + [C: tests/test_preset_manager.py:test_delete_preset, tests/test_presets.py:TestPresetManager.test_delete_preset] + """ path = self._get_path(scope) data = self._read_raw(path) if "presets" in data and name in data["presets"]: @@ -71,6 +86,9 @@ class ToolPresetManager: self._write_raw(path, data) def load_all_bias_profiles(self) -> Dict[str, BiasProfile]: + """ + [C: tests/test_tool_preset_manager.py:test_bias_profiles_merged, tests/test_tool_preset_manager.py:test_delete_bias_profile, tests/test_tool_preset_manager.py:test_save_bias_profile] + """ global_path = paths.get_global_tool_presets_path() global_data = self._read_raw(global_path).get("bias_profiles", {}) @@ -95,6 +113,9 @@ class ToolPresetManager: return profiles def save_bias_profile(self, profile: BiasProfile, scope: str = "project") -> None: + """ + [C: tests/test_tool_preset_manager.py:test_save_bias_profile] + """ path = self._get_path(scope) data = self._read_raw(path) if "bias_profiles" not in data: @@ -103,9 +124,11 @@ class ToolPresetManager: self._write_raw(path, data) def delete_bias_profile(self, name: str, scope: str = "project") -> None: + """ + [C: tests/test_tool_preset_manager.py:test_delete_bias_profile] + """ path = self._get_path(scope) data = self._read_raw(path) if "bias_profiles" in data and name in data["bias_profiles"]: del data["bias_profiles"][name] self._write_raw(path, data) - diff --git a/src/workspace_manager.py b/src/workspace_manager.py index 9e44bef..6cd915c 100644 --- a/src/workspace_manager.py +++ b/src/workspace_manager.py @@ -25,7 +25,10 @@ class WorkspaceManager: raise ValueError("Invalid scope, must be 'global' or 'project'") def load_all_profiles(self) -> Dict[str, WorkspaceProfile]: - """Merges global and project profiles into a single dictionary.""" + """ + Merges global and project profiles into a single dictionary. + [C: tests/test_workspace_manager.py:test_delete_profile, tests/test_workspace_manager.py:test_load_all_profiles_merged, tests/test_workspace_manager.py:test_save_profile_global_and_project] + """ profiles = {} global_path = paths.get_global_workspace_profiles_path() @@ -42,6 +45,9 @@ class WorkspaceManager: return profiles def save_profile(self, profile: WorkspaceProfile, scope: str = "project") -> None: + """ + [C: tests/test_workspace_manager.py:test_delete_profile, tests/test_workspace_manager.py:test_save_profile_global_and_project] + """ path = self._get_path(scope) data = self._load_file(path) if "profiles" not in data: @@ -51,6 +57,9 @@ class WorkspaceManager: self._save_file(path, data) def delete_profile(self, name: str, scope: str = "project") -> None: + """ + [C: tests/test_workspace_manager.py:test_delete_profile] + """ path = self._get_path(scope) data = self._load_file(path) if "profiles" in data and name in data["profiles"]: @@ -69,4 +78,4 @@ class WorkspaceManager: def _save_file(self, path: Path, data: Dict[str, Any]) -> None: path.parent.mkdir(parents=True, exist_ok=True) with open(path, "wb") as f: - tomli_w.dump(data, f) + tomli_w.dump(data, f) \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index 2180493..5afb6b4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -28,6 +28,9 @@ class VerificationLogger: self.logs_dir.mkdir(parents=True, exist_ok=True) def log_state(self, field: str, before: Any, after: Any) -> None: + """ + [C: tests/test_ai_style_formatter.py:test_multiple_top_level_definitions, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_conductor_tech_lead.py:test_topological_sort_vlog, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_tier4_interceptor.py:test_run_powershell_qa_callback_on_failure, tests/test_vlogger_availability.py:test_vlogger_available] + """ delta = "" if isinstance(before, (int, float)) and isinstance(after, (int, float)): diff = after - before @@ -40,6 +43,9 @@ class VerificationLogger: }) def finalize(self, title: str, status: str, result_msg: str) -> None: + """ + [C: tests/test_ai_style_formatter.py:test_multiple_top_level_definitions, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_conductor_tech_lead.py:test_topological_sort_vlog, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_tier4_interceptor.py:test_end_to_end_tier4_integration, tests/test_tier4_interceptor.py:test_run_powershell_qa_callback_on_failure, tests/test_tier4_interceptor.py:test_run_powershell_qa_callback_on_stderr_only, tests/test_vlogger_availability.py:test_vlogger_available] + """ round(time.time() - self.start_time, 2) log_file = self.logs_dir / f"{self.script_name}.txt" with open(log_file, "w", encoding="utf-8") as f: @@ -57,7 +63,8 @@ class VerificationLogger: @pytest.fixture(autouse=True) def reset_paths() -> Generator[None, None, None]: """ - Autouse fixture that resets the paths global state before each test. + + Autouse fixture that resets the paths global state before each test. """ from src import paths paths.reset_resolved() @@ -67,8 +74,9 @@ def reset_paths() -> Generator[None, None, None]: @pytest.fixture(autouse=True) def reset_ai_client() -> Generator[None, None, None]: """ - Autouse fixture that resets the ai_client global state before each test. - This is critical for preventing state pollution between tests. + + Autouse fixture that resets the ai_client global state before each test. + This is critical for preventing state pollution between tests. """ from src import ai_client from src import mcp_client @@ -115,7 +123,8 @@ def kill_process_tree(pid: int | None) -> None: @pytest.fixture def mock_app() -> Generator[App, None, None]: """ - Mock version of the App for simple unit tests that don't need a loop. + + Mock version of the App for simple unit tests that don't need a loop. """ with ( patch('src.models.load_config', return_value={ @@ -146,8 +155,10 @@ def mock_app() -> Generator[App, None, None]: @pytest.fixture def app_instance() -> Generator[App, None, None]: """ - Centralized App instance with all external side effects mocked. - Matches the pattern used in test_token_viz.py and test_gui_phase4.py. + + Centralized App instance with all external side effects mocked. + Matches the pattern used in test_token_viz.py and test_gui_phase4.py. + [C: tests/test_gui2_events.py:test_app_subscribes_to_events] """ with ( patch('src.models.load_config', return_value={ @@ -180,9 +191,10 @@ def app_instance() -> Generator[App, None, None]: @pytest.fixture(scope="session") def live_gui() -> Generator[tuple[subprocess.Popen, str], None, None]: """ - Session-scoped fixture that starts sloppy.py with --enable-test-hooks. - Includes high-signal environment telemetry and workspace isolation. - """ + + Session-scoped fixture that starts sloppy.py with --enable-test-hooks. + Includes high-signal environment telemetry and workspace isolation. + """ gui_script = os.path.abspath("sloppy.py") diag = VerificationLogger("live_gui_startup", "live_gui_diag") diag.log_state("GUI Script", "N/A", "gui_2.py") @@ -347,4 +359,4 @@ def live_gui() -> Generator[tuple[subprocess.Popen, str], None, None]: except PermissionError: time.sleep(0.5) except: - break + break \ No newline at end of file diff --git a/tests/smoke_status_hook.py b/tests/smoke_status_hook.py index cd51f00..2648566 100644 --- a/tests/smoke_status_hook.py +++ b/tests/smoke_status_hook.py @@ -9,7 +9,10 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) from src.api_hook_client import ApiHookClient def wait_for_value(client, field, expected, timeout=5): - """Polls the GUI state until a field matches the expected value.""" + """ + Polls the GUI state until a field matches the expected value. + [C: tests/test_live_workflow.py:test_full_live_workflow] + """ start = time.time() while time.time() - start < timeout: val = client.get_value(field) @@ -32,4 +35,4 @@ def test_status_hook(live_gui) -> None: # 3. Set mma_status to 'hook_mma_test' client.set_value('mma_status', 'hook_mma_test') # 4. Verify via get_value('mma_status') == 'hook_mma_test' (with retry) - assert wait_for_value(client, 'mma_status', 'hook_mma_test'), f"Failed to set mma_status to hook_mma_test. Current value: {client.get_value('mma_status')}" + assert wait_for_value(client, 'mma_status', 'hook_mma_test'), f"Failed to set mma_status to hook_mma_test. Current value: {client.get_value('mma_status')}" \ No newline at end of file diff --git a/tests/test_ai_client_list_models.py b/tests/test_ai_client_list_models.py index b0b7e86..f046857 100644 --- a/tests/test_ai_client_list_models.py +++ b/tests/test_ai_client_list_models.py @@ -2,9 +2,10 @@ from src import ai_client def test_list_models_gemini_cli() -> None: """ - Verifies that 'ai_client.list_models' correctly returns a list of models - for the 'gemini_cli' provider. - """ + + Verifies that 'ai_client.list_models' correctly returns a list of models + for the 'gemini_cli' provider. + """ models = ai_client.list_models("gemini_cli") assert "gemini-3.1-pro-preview" in models assert "gemini-3-flash-preview" in models @@ -12,4 +13,4 @@ def test_list_models_gemini_cli() -> None: assert "gemini-2.5-flash" in models assert "gemini-2.0-flash" in models assert "gemini-2.5-flash-lite" in models - assert len(models) == 6 + assert len(models) == 6 \ No newline at end of file diff --git a/tests/test_ai_settings_layout.py b/tests/test_ai_settings_layout.py index 83e1a7e..a461eba 100644 --- a/tests/test_ai_settings_layout.py +++ b/tests/test_ai_settings_layout.py @@ -55,4 +55,4 @@ def test_set_params_via_custom_callback(live_gui) -> None: break time.sleep(0.5) - assert success, f"Params did not update via custom_callback. Got: {state}" + assert success, f"Params did not update via custom_callback. Got: {state}" \ No newline at end of file diff --git a/tests/test_api_hook_client.py b/tests/test_api_hook_client.py index d493fcf..e411c06 100644 --- a/tests/test_api_hook_client.py +++ b/tests/test_api_hook_client.py @@ -81,4 +81,4 @@ def test_get_node_status() -> None: } status = client.get_node_status("T1") assert status["status"] == "todo" - mock_make.assert_any_call('GET', '/api/mma/node/T1') + mock_make.assert_any_call('GET', '/api/mma/node/T1') \ No newline at end of file diff --git a/tests/test_app_controller_offloading.py b/tests/test_app_controller_offloading.py index 26d6182..3c679d0 100644 --- a/tests/test_app_controller_offloading.py +++ b/tests/test_app_controller_offloading.py @@ -36,7 +36,8 @@ def app_controller(tmp_session_dir): def test_on_comms_entry_tool_result_offloading(app_controller, tmp_session_dir): """ - Test that _on_comms_entry offloads tool_result output to a separate file. + + Test that _on_comms_entry offloads tool_result output to a separate file. """ output_content = "This is a large tool output that should be offloaded." entry = { @@ -81,7 +82,8 @@ def test_on_comms_entry_tool_result_offloading(app_controller, tmp_session_dir): def test_on_tool_log_offloading(app_controller, tmp_session_dir): """ - Test that _on_tool_log calls session_logger.log_tool_call and log_tool_output. + + Test that _on_tool_log calls session_logger.log_tool_call and log_tool_output. """ script = "Get-Process" result = "Process list..." @@ -107,4 +109,4 @@ def test_on_tool_log_offloading(app_controller, tmp_session_dir): assert len(app_controller._pending_tool_calls) == 1 assert app_controller._pending_tool_calls[0]["script"] == script assert app_controller._pending_tool_calls[0]["result"] == result - assert app_controller._pending_tool_calls[0]["source_tier"] == "Tier 3" + assert app_controller._pending_tool_calls[0]["source_tier"] == "Tier 3" \ No newline at end of file diff --git a/tests/test_arch_boundary_phase1.py b/tests/test_arch_boundary_phase1.py index 086eb32..5cc4651 100644 --- a/tests/test_arch_boundary_phase1.py +++ b/tests/test_arch_boundary_phase1.py @@ -47,4 +47,4 @@ class TestArchBoundaryPhase1(unittest.TestCase): with open("scripts/claude_mma_exec.py", "r", encoding="utf-8") as f: content = f.read() self.assertNotIn("C:\\Users\\Ed", content) - self.assertNotIn("/Users/ed", content) + self.assertNotIn("/Users/ed", content) \ No newline at end of file diff --git a/tests/test_arch_boundary_phase2.py b/tests/test_arch_boundary_phase2.py index b5284f4..6f51f6b 100644 --- a/tests/test_arch_boundary_phase2.py +++ b/tests/test_arch_boundary_phase2.py @@ -97,4 +97,4 @@ class TestArchBoundaryPhase2(unittest.TestCase): self.assertTrue(ai_client._is_mutating_tool(t)) self.assertFalse(ai_client._is_mutating_tool("read_file")) - self.assertFalse(ai_client._is_mutating_tool("list_directory")) + self.assertFalse(ai_client._is_mutating_tool("list_directory")) \ No newline at end of file diff --git a/tests/test_arch_boundary_phase3.py b/tests/test_arch_boundary_phase3.py index 4dfea2d..8a23771 100644 --- a/tests/test_arch_boundary_phase3.py +++ b/tests/test_arch_boundary_phase3.py @@ -88,4 +88,4 @@ class TestArchBoundaryPhase3(unittest.TestCase): engine = ExecutionEngine(dag) engine.tick() - self.assertEqual(t2.status, "blocked") + self.assertEqual(t2.status, "blocked") \ No newline at end of file diff --git a/tests/test_ast_parser.py b/tests/test_ast_parser.py index 7f8af49..92f4290 100644 --- a/tests/test_ast_parser.py +++ b/tests/test_ast_parser.py @@ -324,4 +324,3 @@ public: assert 'int y = 2;' in updated assert 'int x = 1;' not in updated assert 'class MyClass {' in updated - diff --git a/tests/test_async_tools.py b/tests/test_async_tools.py index a7b3bb8..b770d7d 100644 --- a/tests/test_async_tools.py +++ b/tests/test_async_tools.py @@ -8,8 +8,9 @@ from src import mcp_client @pytest.mark.asyncio async def test_execute_tool_calls_concurrently_timing(): """ - Verifies that _execute_tool_calls_concurrently runs tools in parallel. - Total time should be approx 0.5s for 3 tools each taking 0.5s. + + Verifies that _execute_tool_calls_concurrently runs tools in parallel. + Total time should be approx 0.5s for 3 tools each taking 0.5s. """ # 1. Setup mock tool calls (Gemini style) class MockGeminiCall: @@ -65,8 +66,9 @@ async def test_execute_tool_calls_concurrently_timing(): @pytest.mark.asyncio async def test_execute_tool_calls_concurrently_exception_handling(): """ - Verifies that if one tool call fails, it doesn't crash the whole group if caught, - but currently gather is used WITHOUT return_exceptions=True, so it should re-raise. + + Verifies that if one tool call fails, it doesn't crash the whole group if caught, + but currently gather is used WITHOUT return_exceptions=True, so it should re-raise. """ class MockGeminiCall: def __init__(self, name, args): @@ -97,4 +99,4 @@ async def test_execute_tool_calls_concurrently_exception_handling(): qa_callback=None, r_idx=0, provider="gemini" - ) + ) \ No newline at end of file diff --git a/tests/test_cli_tool_bridge_mapping.py b/tests/test_cli_tool_bridge_mapping.py index 0866b74..20143ad 100644 --- a/tests/test_cli_tool_bridge_mapping.py +++ b/tests/test_cli_tool_bridge_mapping.py @@ -21,9 +21,10 @@ class TestCliToolBridgeMapping(unittest.TestCase): @patch('api_hook_client.ApiHookClient.request_confirmation') def test_mapping_from_api_format(self, mock_request: MagicMock, mock_stdout: MagicMock, mock_stdin: MagicMock) -> None: """ - Verify that bridge correctly maps 'id', 'name', 'input' (Gemini API format) - into tool_name and tool_input for the hook client. - """ + + Verify that bridge correctly maps 'id', 'name', 'input' (Gemini API format) + into tool_name and tool_input for the hook client. + """ api_tool_call = { 'id': 'call123', 'name': 'read_file', @@ -46,4 +47,4 @@ class TestCliToolBridgeMapping(unittest.TestCase): self.assertEqual(output.get('decision'), 'allow') if __name__ == '__main__': - unittest.main() + unittest.main() \ No newline at end of file diff --git a/tests/test_conductor_abort_event.py b/tests/test_conductor_abort_event.py index c78cf98..0d4b8d5 100644 --- a/tests/test_conductor_abort_event.py +++ b/tests/test_conductor_abort_event.py @@ -6,7 +6,8 @@ import threading def test_conductor_abort_event_populated(): """ - Test that ConductorEngine populates _abort_events when spawning a worker. + + Test that ConductorEngine populates _abort_events when spawning a worker. """ # 1. Mock WorkerPool.spawn to return a mock thread with patch('src.multi_agent_conductor.WorkerPool.spawn') as mock_spawn: @@ -29,4 +30,4 @@ def test_conductor_abort_event_populated(): # 5. Assert that self._abort_events has an entry for the ticket ID assert ticket_id in engine._abort_events - assert isinstance(engine._abort_events[ticket_id], threading.Event) + assert isinstance(engine._abort_events[ticket_id], threading.Event) \ No newline at end of file diff --git a/tests/test_conductor_api_hook_integration.py b/tests/test_conductor_api_hook_integration.py index f9c3e3d..e7c2da2 100644 --- a/tests/test_conductor_api_hook_integration.py +++ b/tests/test_conductor_api_hook_integration.py @@ -3,8 +3,9 @@ from src.api_hook_client import ApiHookClient def simulate_conductor_phase_completion(client: ApiHookClient, track_id: str, phase_name: str) -> bool: """ - Simulates the Conductor agent's logic for phase completion using ApiHookClient. - """ + + Simulates the Conductor agent's logic for phase completion using ApiHookClient. + """ try: # 1. Poll for state state = client.get_gui_state() @@ -22,8 +23,10 @@ def simulate_conductor_phase_completion(client: ApiHookClient, track_id: str, ph return False def test_conductor_integrates_api_hook_client_for_verification(live_gui) -> None: - """Verify that Conductor's simulated phase completion logic properly integrates - with the ApiHookClient and the live Hook Server.""" + """ + Verify that Conductor's simulated phase completion logic properly integrates + with the ApiHookClient and the live Hook Server. + """ client = ApiHookClient() assert client.wait_for_server(timeout=10) @@ -44,4 +47,4 @@ def test_conductor_handles_api_hook_connection_error() -> None: """Verify Conductor handles a simulated API hook connection error (server down).""" client = ApiHookClient(base_url="http://127.0.0.1:9999") # Invalid port result = simulate_conductor_phase_completion(client, "any", "any") - assert result is False + assert result is False \ No newline at end of file diff --git a/tests/test_conductor_engine_abort.py b/tests/test_conductor_engine_abort.py index 7c763ec..2d3eef6 100644 --- a/tests/test_conductor_engine_abort.py +++ b/tests/test_conductor_engine_abort.py @@ -7,7 +7,8 @@ from src.models import Track def test_conductor_engine_initializes_empty_worker_and_abort_dicts() -> None: """ - Test that ConductorEngine correctly initializes _active_workers and _abort_events as empty dictionaries. + + Test that ConductorEngine correctly initializes _active_workers and _abort_events as empty dictionaries. """ # Mock the track object mock_track = MagicMock(spec=Track) @@ -22,8 +23,9 @@ def test_conductor_engine_initializes_empty_worker_and_abort_dicts() -> None: def test_kill_worker_sets_abort_and_joins_thread() -> None: """ - Test kill_worker: mock a running thread in _active_workers, call kill_worker, - assert abort_event is set and thread is joined. + + Test kill_worker: mock a running thread in _active_workers, call kill_worker, + assert abort_event is set and thread is joined. """ mock_track = MagicMock(spec=Track) mock_track.tickets = [] @@ -35,6 +37,9 @@ def test_kill_worker_sets_abort_and_joins_thread() -> None: # Create a thread that waits for the abort event 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] + """ abort_event.wait(timeout=2.0) thread = threading.Thread(target=worker) @@ -50,4 +55,4 @@ def test_kill_worker_sets_abort_and_joins_thread() -> None: assert abort_event.is_set() assert not thread.is_alive() with engine._workers_lock: - assert ticket_id not in engine._active_workers + assert ticket_id not in engine._active_workers \ No newline at end of file diff --git a/tests/test_conductor_engine_v2.py b/tests/test_conductor_engine_v2.py index 471179c..0ec31fa 100644 --- a/tests/test_conductor_engine_v2.py +++ b/tests/test_conductor_engine_v2.py @@ -9,8 +9,9 @@ from src import ai_client def test_conductor_engine_initialization() -> None: """ - Test that ConductorEngine can be initialized with a Track. - """ + + Test that ConductorEngine can be initialized with a Track. + """ track = Track(id="test_track", description="Test Track") from src.multi_agent_conductor import ConductorEngine engine = ConductorEngine(track=track, auto_queue=True) @@ -18,8 +19,9 @@ def test_conductor_engine_initialization() -> None: def test_conductor_engine_run_executes_tickets_in_order(monkeypatch: pytest.MonkeyPatch, vlogger) -> None: """ - Test that run iterates through executable tickets and calls the worker lifecycle. - """ + + Test that run iterates through executable tickets and calls the worker lifecycle. + """ ticket1 = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1") ticket2 = Ticket(id="T2", description="Task 2", status="todo", assigned_to="worker2", depends_on=["T1"]) track = Track(id="track1", description="Track 1", tickets=[ticket1, ticket2]) @@ -64,8 +66,9 @@ def test_conductor_engine_run_executes_tickets_in_order(monkeypatch: pytest.Monk def test_run_worker_lifecycle_calls_ai_client_send(monkeypatch: pytest.MonkeyPatch) -> None: """ - Test that run_worker_lifecycle triggers the AI client and updates ticket status on success. - """ + + Test that run_worker_lifecycle triggers the AI client and updates ticket status on success. + """ ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1") context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[]) from src.multi_agent_conductor import run_worker_lifecycle @@ -84,8 +87,9 @@ def test_run_worker_lifecycle_calls_ai_client_send(monkeypatch: pytest.MonkeyPat def test_run_worker_lifecycle_context_injection(monkeypatch: pytest.MonkeyPatch) -> None: """ - Test that run_worker_lifecycle can take a context_files list and injects AST views into the prompt. - """ + + Test that run_worker_lifecycle can take a context_files list and injects AST views into the prompt. + """ ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1") context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[]) context_files = ["primary.py", "secondary.py"] @@ -129,8 +133,9 @@ def test_run_worker_lifecycle_context_injection(monkeypatch: pytest.MonkeyPatch) def test_run_worker_lifecycle_handles_blocked_response(monkeypatch: pytest.MonkeyPatch) -> None: """ - Test that run_worker_lifecycle marks the ticket as blocked if the AI indicates it cannot proceed. - """ + + Test that run_worker_lifecycle marks the ticket as blocked if the AI indicates it cannot proceed. + """ ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1") context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[]) from src.multi_agent_conductor import run_worker_lifecycle @@ -145,10 +150,11 @@ def test_run_worker_lifecycle_handles_blocked_response(monkeypatch: pytest.Monke def test_run_worker_lifecycle_step_mode_confirmation(monkeypatch: pytest.MonkeyPatch) -> None: """ - Test that run_worker_lifecycle passes confirm_execution to ai_client.send when step_mode is True. - Verify that if confirm_execution is called (simulated by mocking ai_client.send to call its callback), - the flow works as expected. - """ + + Test that run_worker_lifecycle passes confirm_execution to ai_client.send when step_mode is True. + Verify that if confirm_execution is called (simulated by mocking ai_client.send to call its callback), + the flow works as expected. + """ ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1", step_mode=True) context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[]) from src.multi_agent_conductor import run_worker_lifecycle @@ -181,9 +187,10 @@ def test_run_worker_lifecycle_step_mode_confirmation(monkeypatch: pytest.MonkeyP def test_run_worker_lifecycle_step_mode_rejection(monkeypatch: pytest.MonkeyPatch) -> None: """ - Verify that if confirm_execution returns False, the logic (in ai_client, which we simulate here) - would prevent execution. In run_worker_lifecycle, we just check if it's passed. - """ + + Verify that if confirm_execution returns False, the logic (in ai_client, which we simulate here) + would prevent execution. In run_worker_lifecycle, we just check if it's passed. + """ ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1", step_mode=True) context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[]) from src.multi_agent_conductor import run_worker_lifecycle @@ -205,8 +212,9 @@ def test_run_worker_lifecycle_step_mode_rejection(monkeypatch: pytest.MonkeyPatc def test_conductor_engine_dynamic_parsing_and_execution(monkeypatch: pytest.MonkeyPatch, vlogger) -> None: """ - Test that parse_json_tickets correctly populates the track and run executes them in dependency order. - """ + + Test that parse_json_tickets correctly populates the track and run executes them in dependency order. + """ import json from src.multi_agent_conductor import ConductorEngine track = Track(id="dynamic_track", description="Dynamic Track") @@ -272,9 +280,10 @@ def test_conductor_engine_dynamic_parsing_and_execution(monkeypatch: pytest.Monk def test_run_worker_lifecycle_pushes_response_via_queue(monkeypatch: pytest.MonkeyPatch) -> None: """ - Test that run_worker_lifecycle pushes a 'response' event with the correct stream_id - via _queue_put when event_queue is provided. - """ + + Test that run_worker_lifecycle pushes a 'response' event with the correct stream_id + via _queue_put when event_queue is provided. + """ ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1") context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[]) mock_event_queue = MagicMock() @@ -297,9 +306,10 @@ def test_run_worker_lifecycle_pushes_response_via_queue(monkeypatch: pytest.Monk def test_run_worker_lifecycle_token_usage_from_comms_log(monkeypatch: pytest.MonkeyPatch) -> None: """ - Test that run_worker_lifecycle reads token usage from the comms log and - updates engine.tier_usage['Tier 3'] with real input/output token counts. - """ + + Test that run_worker_lifecycle reads token usage from the comms log and + updates engine.tier_usage['Tier 3'] with real input/output token counts. + """ ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1") context = WorkerContext(ticket_id="T1", model_name="test-model", messages=[]) fake_comms = [ @@ -320,4 +330,4 @@ def test_run_worker_lifecycle_token_usage_from_comms_log(monkeypatch: pytest.Mon mock_spawn.return_value = (True, "prompt", "ctx") run_worker_lifecycle(ticket, context, event_queue=MagicMock(), engine=engine) assert engine.tier_usage["Tier 3"]["input"] == 120 - assert engine.tier_usage["Tier 3"]["output"] == 45 + assert engine.tier_usage["Tier 3"]["output"] == 45 \ No newline at end of file diff --git a/tests/test_dag_engine.py b/tests/test_dag_engine.py index 8fe8b21..ad9f402 100644 --- a/tests/test_dag_engine.py +++ b/tests/test_dag_engine.py @@ -9,7 +9,8 @@ from src.dag_engine import TrackDAG def test_get_ready_tasks_linear(): """ - Verifies ready tasks detection in a simple linear dependency chain. + + Verifies ready tasks detection in a simple linear dependency chain. """ t1 = Ticket(id="T1", description="desc", status="todo", assigned_to="worker1") t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"]) @@ -20,8 +21,9 @@ def test_get_ready_tasks_linear(): def test_get_ready_tasks_branching(): """ - Verifies ready tasks detection in a branching dependency graph where multiple tasks - are unlocked simultaneously after a prerequisite is met. + + Verifies ready tasks detection in a branching dependency graph where multiple tasks + are unlocked simultaneously after a prerequisite is met. """ t1 = Ticket(id="T1", description="desc", status="completed", assigned_to="worker1") t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"]) @@ -35,7 +37,8 @@ def test_get_ready_tasks_branching(): def test_has_cycle_no_cycle(): """ - Validates that an acyclic graph is correctly identified as not having cycles. + + Validates that an acyclic graph is correctly identified as not having cycles. """ t1 = Ticket(id="T1", description="desc", status="todo", assigned_to="worker1") t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"]) @@ -44,7 +47,8 @@ def test_has_cycle_no_cycle(): def test_has_cycle_direct_cycle(): """ - Validates that a direct cycle (A depends on B, B depends on A) is correctly detected. + + Validates that a direct cycle (A depends on B, B depends on A) is correctly detected. """ t1 = Ticket(id="T1", description="desc", status="todo", assigned_to="worker1", depends_on=["T2"]) t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"]) @@ -53,7 +57,8 @@ def test_has_cycle_direct_cycle(): def test_has_cycle_indirect_cycle(): """ - Validates that an indirect cycle (A->B->C->A) is correctly detected. + + Validates that an indirect cycle (A->B->C->A) is correctly detected. """ t1 = Ticket(id="T1", description="desc", status="todo", assigned_to="worker1", depends_on=["T3"]) t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"]) @@ -63,7 +68,8 @@ def test_has_cycle_indirect_cycle(): def test_has_cycle_complex_no_cycle(): """ - Validates cycle detection in a complex graph that merges branches but remains acyclic. + + Validates cycle detection in a complex graph that merges branches but remains acyclic. """ t1 = Ticket(id="T1", description="desc", status="todo", assigned_to="worker1") t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"]) @@ -74,7 +80,8 @@ def test_has_cycle_complex_no_cycle(): def test_get_ready_tasks_multiple_deps(): """ - Validates that a task is not marked ready until ALL of its dependencies are completed. + + Validates that a task is not marked ready until ALL of its dependencies are completed. """ t1 = Ticket(id="T1", description="desc", status="completed", assigned_to="worker1") t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1") @@ -87,7 +94,8 @@ def test_get_ready_tasks_multiple_deps(): def test_topological_sort(): """ - Verifies that tasks are correctly ordered by dependencies regardless of input order. + + Verifies that tasks are correctly ordered by dependencies regardless of input order. """ t1 = Ticket(id="T1", description="desc", status="todo", assigned_to="worker1") t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"]) @@ -98,10 +106,11 @@ def test_topological_sort(): def test_topological_sort_cycle(): """ - Verifies that topological sorting safely aborts and raises ValueError when a cycle is present. + + Verifies that topological sorting safely aborts and raises ValueError when a cycle is present. """ t1 = Ticket(id="T1", description="desc", status="todo", assigned_to="worker1", depends_on=["T2"]) t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"]) dag = TrackDAG([t1, t2]) with pytest.raises(ValueError, match="Dependency cycle detected"): - dag.topological_sort() + dag.topological_sort() \ No newline at end of file diff --git a/tests/test_deepseek_infra.py b/tests/test_deepseek_infra.py index 9f24d17..39334da 100644 --- a/tests/test_deepseek_infra.py +++ b/tests/test_deepseek_infra.py @@ -12,9 +12,10 @@ from src import project_manager def test_credentials_error_mentions_deepseek(monkeypatch: pytest.MonkeyPatch) -> None: """ - Verify that the error message shown when credentials.toml is missing - includes deepseek instructions. - """ + + Verify that the error message shown when credentials.toml is missing + includes deepseek instructions. + """ # Monkeypatch SLOP_CREDENTIALS to a non-existent file monkeypatch.setenv("SLOP_CREDENTIALS", "non_existent_credentials_file.toml") with pytest.raises(FileNotFoundError) as excinfo: @@ -25,32 +26,36 @@ def test_credentials_error_mentions_deepseek(monkeypatch: pytest.MonkeyPatch) -> def test_default_project_includes_reasoning_role() -> None: """ - Verify that 'Reasoning' is included in the default discussion roles - to support DeepSeek-R1 reasoning traces. - """ + + Verify that 'Reasoning' is included in the default discussion roles + to support DeepSeek-R1 reasoning traces. + """ proj = project_manager.default_project("test") roles = proj["discussion"]["roles"] assert "Reasoning" in roles def test_gui_providers_list() -> None: """ - Check if 'deepseek' is in the GUI's provider list. - """ + + Check if 'deepseek' is in the GUI's provider list. + """ from src.models import PROVIDERS assert "deepseek" in PROVIDERS def test_deepseek_model_listing() -> None: """ - Verify that list_models for deepseek returns expected models. - """ + + Verify that list_models for deepseek returns expected models. + """ models = ai_client.list_models("deepseek") assert "deepseek-chat" in models assert "deepseek-reasoner" in models def test_gui_provider_list_via_hooks(live_gui: Any) -> None: """ - Verify 'deepseek' is present in the GUI provider list using API hooks. - """ + + Verify 'deepseek' is present in the GUI provider list using API hooks. + """ from api_hook_client import ApiHookClient import time client = ApiHookClient() @@ -58,4 +63,4 @@ def test_gui_provider_list_via_hooks(live_gui: Any) -> None: # Attempt to set provider to deepseek to verify it's an allowed value client.set_value('current_provider', 'deepseek') time.sleep(0.5) - assert client.get_value('current_provider') == 'deepseek' + assert client.get_value('current_provider') == 'deepseek' \ No newline at end of file diff --git a/tests/test_deepseek_provider.py b/tests/test_deepseek_provider.py index affeecc..b9bba68 100644 --- a/tests/test_deepseek_provider.py +++ b/tests/test_deepseek_provider.py @@ -4,8 +4,9 @@ from src import ai_client def test_deepseek_model_selection() -> None: """ - Verifies that ai_client.set_provider('deepseek', 'deepseek-chat') correctly updates the internal state. - """ + + Verifies that ai_client.set_provider('deepseek', 'deepseek-chat') correctly updates the internal state. + """ ai_client.set_provider("deepseek", "deepseek-chat") assert ai_client._provider == "deepseek" assert ai_client._model == "deepseek-chat" @@ -13,8 +14,9 @@ def test_deepseek_model_selection() -> None: @patch("requests.post") def test_deepseek_completion_logic(mock_post: MagicMock) -> None: """ - Verifies that ai_client.send() correctly calls the DeepSeek API and returns content. - """ + + Verifies that ai_client.send() correctly calls the DeepSeek API and returns content. + """ ai_client.set_provider("deepseek", "deepseek-chat") with patch("src.ai_client._load_credentials", return_value={"deepseek": {"api_key": "test-key"}}): mock_response = MagicMock() @@ -31,8 +33,9 @@ def test_deepseek_completion_logic(mock_post: MagicMock) -> None: @patch("requests.post") def test_deepseek_reasoning_logic(mock_post: MagicMock) -> None: """ - Verifies that reasoning_content is captured and wrapped in tags. - """ + + Verifies that reasoning_content is captured and wrapped in tags. + """ ai_client.set_provider("deepseek", "deepseek-reasoner") with patch("src.ai_client._load_credentials", return_value={"deepseek": {"api_key": "test-key"}}): mock_response = MagicMock() @@ -52,8 +55,9 @@ def test_deepseek_reasoning_logic(mock_post: MagicMock) -> None: @patch("requests.post") def test_deepseek_tool_calling(mock_post: MagicMock) -> None: """ - Verifies that DeepSeek provider correctly identifies and executes tool calls. - """ + + Verifies that DeepSeek provider correctly identifies and executes tool calls. + """ ai_client.set_provider("deepseek", "deepseek-chat") with patch("src.ai_client._load_credentials", return_value={"deepseek": {"api_key": "test-key"}}), \ patch("src.mcp_client.async_dispatch", new_callable=unittest.mock.AsyncMock) as mock_dispatch: @@ -93,8 +97,9 @@ def test_deepseek_tool_calling(mock_post: MagicMock) -> None: @patch("requests.post") def test_deepseek_streaming(mock_post: MagicMock) -> None: """ - Verifies that DeepSeek provider correctly aggregates streaming chunks. - """ + + Verifies that DeepSeek provider correctly aggregates streaming chunks. + """ ai_client.set_provider("deepseek", "deepseek-chat") with patch("src.ai_client._load_credentials", return_value={"deepseek": {"api_key": "test-key"}}): mock_response = MagicMock() @@ -115,8 +120,9 @@ def test_deepseek_streaming(mock_post: MagicMock) -> None: @patch("requests.post") def test_deepseek_payload_verification(mock_post: MagicMock) -> None: """ - Verifies that the correct JSON payload (tools, history, params) is sent to DeepSeek. - """ + + Verifies that the correct JSON payload (tools, history, params) is sent to DeepSeek. + """ ai_client.set_provider("deepseek", "deepseek-chat") ai_client.reset_session() with patch("src.ai_client._load_credentials", return_value={"deepseek": {"api_key": "test-key"}}): @@ -142,8 +148,9 @@ def test_deepseek_payload_verification(mock_post: MagicMock) -> None: @patch("requests.post") def test_deepseek_reasoner_payload_verification(mock_post: MagicMock) -> None: """ - Verifies that deepseek-reasoner payload excludes tools and temperature. - """ + + Verifies that deepseek-reasoner payload excludes tools and temperature. + """ ai_client.set_provider("deepseek", "deepseek-reasoner") ai_client.reset_session() with patch("src.ai_client._load_credentials", return_value={"deepseek": {"api_key": "test-key"}}): @@ -162,4 +169,4 @@ def test_deepseek_reasoner_payload_verification(mock_post: MagicMock) -> None: assert payload["model"] == "deepseek-reasoner" assert "tools" not in payload assert "temperature" not in payload - assert "max_tokens" not in payload + assert "max_tokens" not in payload \ No newline at end of file diff --git a/tests/test_discussion_takes.py b/tests/test_discussion_takes.py index 7acfdc2..59933bf 100644 --- a/tests/test_discussion_takes.py +++ b/tests/test_discussion_takes.py @@ -47,4 +47,4 @@ class TestDiscussionTakes(unittest.TestCase): self.assertEqual(self.project_dict["discussion"]["discussions"][new_id]["history"], ["User: Experimental"]) if __name__ == "__main__": - unittest.main() + unittest.main() \ No newline at end of file diff --git a/tests/test_discussion_takes_gui.py b/tests/test_discussion_takes_gui.py index e1e31ce..9119a49 100644 --- a/tests/test_discussion_takes_gui.py +++ b/tests/test_discussion_takes_gui.py @@ -94,4 +94,4 @@ def test_switching_discussion_via_tabs(app_instance): app_instance._render_discussion_panel() # If implemented with tabs, this should be called - mock_switch.assert_called_with("main_take_1") + mock_switch.assert_called_with("main_take_1") \ No newline at end of file diff --git a/tests/test_extended_sims.py b/tests/test_extended_sims.py index 9d3ea0d..7046c99 100644 --- a/tests/test_extended_sims.py +++ b/tests/test_extended_sims.py @@ -70,4 +70,3 @@ def test_execution_sim_live(live_gui: Any) -> None: sim.run() time.sleep(2) sim.teardown() - diff --git a/tests/test_file_item_model.py b/tests/test_file_item_model.py index a549514..3229616 100644 --- a/tests/test_file_item_model.py +++ b/tests/test_file_item_model.py @@ -41,4 +41,4 @@ def test_file_item_from_dict_defaults(): assert item.path == "test.py" assert item.auto_aggregate is True assert item.force_full is False - assert item.injected_at is None + assert item.injected_at is None \ No newline at end of file diff --git a/tests/test_gemini_metrics.py b/tests/test_gemini_metrics.py index e55e5b2..46e2fb5 100644 --- a/tests/test_gemini_metrics.py +++ b/tests/test_gemini_metrics.py @@ -11,9 +11,10 @@ from src.ai_client import get_gemini_cache_stats, reset_session def test_get_gemini_cache_stats_with_mock_client() -> None: """ - Test that get_gemini_cache_stats correctly processes cache lists - from a mocked client instance. - """ + + Test that get_gemini_cache_stats correctly processes cache lists + from a mocked client instance. + """ # Ensure a clean state before the test by resetting the session reset_session() # 1. Create a mock for the cache object that the client will return @@ -40,4 +41,4 @@ def test_get_gemini_cache_stats_with_mock_client() -> None: assert "cache_count" in stats assert "total_size_bytes" in stats assert stats["cache_count"] == 1 - assert stats["total_size_bytes"] == 1024 + assert stats["total_size_bytes"] == 1024 \ No newline at end of file diff --git a/tests/test_gui2_events.py b/tests/test_gui2_events.py index fffa33e..151bb36 100644 --- a/tests/test_gui2_events.py +++ b/tests/test_gui2_events.py @@ -11,9 +11,10 @@ def app_instance(monkeypatch: pytest.MonkeyPatch) -> type[App]: def test_app_subscribes_to_events(app_instance: type[App]) -> None: """ - This test checks that the App's __init__ method subscribes the necessary - event handlers to the ai_client.events emitter. - """ + + This test checks that the App's __init__ method subscribes the necessary + event handlers to the ai_client.events emitter. + """ with patch.object(ai_client.events, 'on') as mock_on: app = app_instance() mock_on.assert_called() @@ -22,4 +23,4 @@ def test_app_subscribes_to_events(app_instance: type[App]) -> None: assert "request_start" in event_names assert "response_received" in event_names assert "tool_execution" in event_names - # We don't check for __self__ anymore as they might be lambdas + # We don't check for __self__ anymore as they might be lambdas \ No newline at end of file diff --git a/tests/test_gui2_layout.py b/tests/test_gui2_layout.py index 8fddfa2..9755045 100644 --- a/tests/test_gui2_layout.py +++ b/tests/test_gui2_layout.py @@ -2,9 +2,10 @@ from src.gui_2 import App def test_gui2_hubs_exist_in_show_windows(app_instance: App) -> None: """ - Verifies that the new consolidated Hub windows are defined in the App's show_windows. - This ensures they will be available in the 'Windows' menu. - """ + + Verifies that the new consolidated Hub windows are defined in the App's show_windows. + This ensures they will be available in the 'Windows' menu. + """ expected_hubs = [ "Project Settings", "AI Settings", @@ -18,13 +19,14 @@ def test_gui2_hubs_exist_in_show_windows(app_instance: App) -> None: def test_gui2_old_windows_removed_from_show_windows(app_instance: App) -> None: """ - Verifies that the old fragmented windows are removed from show_windows. - Note: Message, Response, and Tool Calls are kept as they are now optional standalone windows. - """ + + Verifies that the old fragmented windows are removed from show_windows. + Note: Message, Response, and Tool Calls are kept as they are now optional standalone windows. + """ old_windows = [ "Projects", "Files", "Screenshots", "Provider", "System Prompts", "Comms History" ] for old_win in old_windows: - assert old_win not in app_instance.show_windows, f"Old window '{old_win}' should have been removed from show_windows" + assert old_win not in app_instance.show_windows, f"Old window '{old_win}' should have been removed from show_windows" \ No newline at end of file diff --git a/tests/test_gui2_mcp.py b/tests/test_gui2_mcp.py index 461df54..7cffefa 100644 --- a/tests/test_gui2_mcp.py +++ b/tests/test_gui2_mcp.py @@ -5,10 +5,11 @@ from src import ai_client def test_mcp_tool_call_is_dispatched(app_instance: App) -> None: """ - This test verifies that when the AI returns a tool call for an MCP function, - the ai_client correctly dispatches it to mcp_client. - This will fail until mcp_client is properly integrated. - """ + + This test verifies that when the AI returns a tool call for an MCP function, + the ai_client correctly dispatches it to mcp_client. + This will fail until mcp_client is properly integrated. + """ # 1. Define the mock tool call from the AI mock_fc = MagicMock() mock_fc.name = "read_file" @@ -50,4 +51,4 @@ def test_mcp_tool_call_is_dispatched(app_instance: App) -> None: discussion_history="" ) # 6. Assert that the MCP dispatch function was called - mock_dispatch.assert_called_once_with("read_file", {"file_path": "test.txt"}) + mock_dispatch.assert_called_once_with("read_file", {"file_path": "test.txt"}) \ No newline at end of file diff --git a/tests/test_gui2_parity.py b/tests/test_gui2_parity.py index bcb3545..01ffbc5 100644 --- a/tests/test_gui2_parity.py +++ b/tests/test_gui2_parity.py @@ -25,8 +25,9 @@ def cleanup_callback_file() -> None: def test_gui2_set_value_hook_works(live_gui: Any) -> None: """ - Tests that the 'set_value' GUI hook is correctly implemented. - """ + + Tests that the 'set_value' GUI hook is correctly implemented. + """ client = ApiHookClient() assert client.wait_for_server(timeout=10) test_value = f"New value set by test: {uuid.uuid4()}" @@ -40,8 +41,9 @@ def test_gui2_set_value_hook_works(live_gui: Any) -> None: def test_gui2_click_hook_works(live_gui: Any) -> None: """ - Tests that the 'click' GUI hook for the 'Reset' button is implemented. - """ + + Tests that the 'click' GUI hook for the 'Reset' button is implemented. + """ client = ApiHookClient() assert client.wait_for_server(timeout=10) # First, set some state that 'Reset' would clear. @@ -57,8 +59,9 @@ def test_gui2_click_hook_works(live_gui: Any) -> None: def test_gui2_custom_callback_hook_works(live_gui: Any) -> None: """ - Tests that the 'custom_callback' GUI hook is correctly implemented. - """ + + Tests that the 'custom_callback' GUI hook is correctly implemented. + """ client = ApiHookClient() assert client.wait_for_server(timeout=10) test_data = f"Callback executed: {uuid.uuid4()}" @@ -76,4 +79,3 @@ def test_gui2_custom_callback_hook_works(live_gui: Any) -> None: with open(temp_workspace_file, "r") as f: content = f.read() assert content == test_data, "Callback executed, but file content is incorrect." - diff --git a/tests/test_gui2_performance.py b/tests/test_gui2_performance.py index 2051c4d..279a048 100644 --- a/tests/test_gui2_performance.py +++ b/tests/test_gui2_performance.py @@ -19,8 +19,9 @@ _shared_metrics = {} def test_performance_benchmarking(live_gui: tuple) -> None: """ - Collects performance metrics for the current GUI script over a 5-second window. - Ensures the application does not lock up and can report its internal state. + + Collects performance metrics for the current GUI script over a 5-second window. + Ensures the application does not lock up and can report its internal state. """ process, gui_script = live_gui client = ApiHookClient() @@ -65,8 +66,9 @@ def test_performance_benchmarking(live_gui: tuple) -> None: def test_performance_baseline_check() -> None: """ - Verifies that we have successfully collected performance metrics for sloppy.py - and that they meet the minimum 30 FPS baseline. + + Verifies that we have successfully collected performance metrics for sloppy.py + and that they meet the minimum 30 FPS baseline. """ # Key is full path, find it by basename gui_key = next((k for k in _shared_metrics if "sloppy.py" in k), None) @@ -77,4 +79,4 @@ def test_performance_baseline_check() -> None: # A 0 FPS indicates the render loop is completely frozen or the API hook is dead. assert gui2_m["avg_fps"] > 0, "No performance metrics collected - GUI may be frozen" assert gui2_m["avg_fps"] >= 30 - assert gui2_m["avg_ft"] <= 33.3 + assert gui2_m["avg_ft"] <= 33.3 \ No newline at end of file diff --git a/tests/test_gui_context_presets.py b/tests/test_gui_context_presets.py index eff65eb..f9d1503 100644 --- a/tests/test_gui_context_presets.py +++ b/tests/test_gui_context_presets.py @@ -32,4 +32,4 @@ def test_gui_context_preset_save_load(live_gui) -> None: context = client.get_context_state() loaded_files = [f["path"] if isinstance(f, dict) else str(f) for f in context.get("files", [])] assert loaded_files == test_files - assert context.get("screenshots", []) == test_screenshots + assert context.get("screenshots", []) == test_screenshots \ No newline at end of file diff --git a/tests/test_gui_diagnostics.py b/tests/test_gui_diagnostics.py index dd0c37b..5c921a7 100644 --- a/tests/test_gui_diagnostics.py +++ b/tests/test_gui_diagnostics.py @@ -14,9 +14,10 @@ def test_diagnostics_panel_initialization(app_instance: Any) -> None: def test_diagnostics_history_updates(app_instance: Any) -> None: """ - Verifies that the internal performance history is updated correctly. - This logic is inside the render loop in gui_2.py, but we can test - the data structure and initialization. + + Verifies that the internal performance history is updated correctly. + This logic is inside the render loop in gui_2.py, but we can test + the data structure and initialization. """ assert "fps" in app_instance.perf_history - assert len(app_instance.perf_history["fps"]) == 100 + assert len(app_instance.perf_history["fps"]) == 100 \ No newline at end of file diff --git a/tests/test_gui_paths.py b/tests/test_gui_paths.py index 3aafece..a238781 100644 --- a/tests/test_gui_paths.py +++ b/tests/test_gui_paths.py @@ -12,6 +12,9 @@ class MockApp: self.ai_status = "" def init_state(self): + """ + [C: tests/test_system_prompt_exposure.py:TestSystemPromptExposure.test_app_controller_init_state_loads_prompts] + """ pass from src.gui_2 import App @@ -38,4 +41,4 @@ def test_save_paths(): mock_copy.assert_called_once() assert 'applied' in mock_app.ai_status mock_reset.assert_called_once() - mock_init.assert_called_once() + mock_init.assert_called_once() \ No newline at end of file diff --git a/tests/test_gui_performance_requirements.py b/tests/test_gui_performance_requirements.py index 175632a..9b69b34 100644 --- a/tests/test_gui_performance_requirements.py +++ b/tests/test_gui_performance_requirements.py @@ -10,8 +10,9 @@ from api_hook_client import ApiHookClient def test_idle_performance_requirements(live_gui) -> None: """ - Requirement: GUI must maintain stable performance on idle. - """ + + Requirement: GUI must maintain stable performance on idle. + """ # Warmup to ensure GUI is ready time.sleep(5.0) client = ApiHookClient() @@ -39,4 +40,4 @@ def test_idle_performance_requirements(live_gui) -> None: print("[Warning] Frame time is 0.0. This is expected in headless CI/CD environments.") print(f"[Test] Valid frame time samples: {valid_ft_count}/5") # In some CI environments without a real display, frame time might remain 0 - # but we've verified the hook is returning the dictionary. + # but we've verified the hook is returning the dictionary. \ No newline at end of file diff --git a/tests/test_gui_phase3.py b/tests/test_gui_phase3.py index 7e4239d..df84150 100644 --- a/tests/test_gui_phase3.py +++ b/tests/test_gui_phase3.py @@ -11,8 +11,9 @@ from src import paths def test_track_proposal_editing(app_instance): """ - Verifies the structural integrity of track proposal items. - Ensures that track proposals can be edited and removed from the active list. + + Verifies the structural integrity of track proposal items. + Ensures that track proposals can be edited and removed from the active list. """ app_instance.proposed_tracks = [ {"title": "Old Title", "goal": "Old Goal"}, @@ -33,8 +34,9 @@ def test_track_proposal_editing(app_instance): def test_conductor_setup_scan(app_instance, tmp_path, monkeypatch): """ - Verifies that the conductor setup scan properly iterates through the conductor directory, - counts files and lines, and identifies active tracks. + + Verifies that the conductor setup scan properly iterates through the conductor directory, + counts files and lines, and identifies active tracks. """ old_cwd = os.getcwd() os.chdir(tmp_path) @@ -60,8 +62,9 @@ def test_conductor_setup_scan(app_instance, tmp_path, monkeypatch): def test_create_track(app_instance, tmp_path): """ - Verifies that _cb_create_track properly creates the track folder - and populates the necessary boilerplate files (spec.md, plan.md, metadata.json). + + Verifies that _cb_create_track properly creates the track folder + and populates the necessary boilerplate files (spec.md, plan.md, metadata.json). """ old_cwd = os.getcwd() os.chdir(tmp_path) @@ -89,4 +92,3 @@ def test_create_track(app_instance, tmp_path): assert data['id'] == track_dir.name finally: os.chdir(old_cwd) - diff --git a/tests/test_gui_startup_smoke.py b/tests/test_gui_startup_smoke.py index 265829a..83e3a8f 100644 --- a/tests/test_gui_startup_smoke.py +++ b/tests/test_gui_startup_smoke.py @@ -2,7 +2,8 @@ import time def test_gui_startup_smoke(live_gui): """ - Smoke test to ensure the GUI starts and remains running. + + Smoke test to ensure the GUI starts and remains running. """ proc, _ = live_gui @@ -13,4 +14,4 @@ def test_gui_startup_smoke(live_gui): time.sleep(2) # Verify it's still running after 2 seconds - assert proc.poll() is None, "GUI process crashed within 2 seconds of startup" + assert proc.poll() is None, "GUI process crashed within 2 seconds of startup" \ No newline at end of file diff --git a/tests/test_gui_synthesis.py b/tests/test_gui_synthesis.py index 6df1f6a..78e8782 100644 --- a/tests/test_gui_synthesis.py +++ b/tests/test_gui_synthesis.py @@ -53,4 +53,4 @@ def test_render_synthesis_panel(app_instance): mock_imgui.input_text_multiline.assert_called_with("##synthesis_prompt", app_instance.ui_synthesis_prompt, ANY) # 3. Assert imgui.button is called for 'Generate Synthesis' - mock_imgui.button.assert_any_call("Generate Synthesis") + mock_imgui.button.assert_any_call("Generate Synthesis") \ No newline at end of file diff --git a/tests/test_gui_text_viewer.py b/tests/test_gui_text_viewer.py index 5e194ad..8760ac9 100644 --- a/tests/test_gui_text_viewer.py +++ b/tests/test_gui_text_viewer.py @@ -4,7 +4,8 @@ from src.api_hook_client import ApiHookClient def test_text_viewer_state_update(live_gui) -> None: """ - Verifies that we can set text viewer state and it is reflected in GUI state. + + Verifies that we can set text viewer state and it is reflected in GUI state. """ client = ApiHookClient() label = "Test Viewer Label" @@ -21,4 +22,4 @@ def test_text_viewer_state_update(live_gui) -> None: assert state is not None assert state.get('show_text_viewer') == True assert state.get('text_viewer_title') == label - assert state.get('text_viewer_type') == text_type + assert state.get('text_viewer_type') == text_type \ No newline at end of file diff --git a/tests/test_gui_updates.py b/tests/test_gui_updates.py index 9e1e68b..efc82e5 100644 --- a/tests/test_gui_updates.py +++ b/tests/test_gui_updates.py @@ -16,9 +16,10 @@ from src.gui_2 import App def test_telemetry_data_updates_correctly(app_instance: Any) -> None: """ - Tests that the _refresh_api_metrics method correctly updates - the internal state for display by querying the ai_client. - Verifies the boundary between GUI state and API state. + + Tests that the _refresh_api_metrics method correctly updates + the internal state for display by querying the ai_client. + Verifies the boundary between GUI state and API state. """ # 1. Set the provider to anthropic app_instance._current_provider = "anthropic" @@ -41,9 +42,10 @@ def test_telemetry_data_updates_correctly(app_instance: Any) -> None: def test_performance_history_updates(app_instance: Any) -> None: """ - Verify the data structure that feeds the sparkline. - This ensures that the rolling buffer for performance telemetry maintains - the correct size and default initialization to prevent GUI rendering crashes. + + Verify the data structure that feeds the sparkline. + This ensures that the rolling buffer for performance telemetry maintains + the correct size and default initialization to prevent GUI rendering crashes. """ # ANTI-SIMPLIFICATION: Verifying exactly 100 elements ensures the sparkline won't overflow assert len(app_instance.perf_history["frame_time"]) == 100 @@ -51,9 +53,10 @@ def test_performance_history_updates(app_instance: Any) -> None: def test_gui_updates_on_event(app_instance: App) -> None: """ - Verifies that when an API event is received (e.g. from ai_client), - the _on_api_event handler correctly updates internal metrics and - queues the update to be processed by the GUI event loop. + + Verifies that when an API event is received (e.g. from ai_client), + the _on_api_event handler correctly updates internal metrics and + queues the update to be processed by the GUI event loop. """ mock_stats = {"percentage": 50.0, "current": 500, "limit": 1000} app_instance.last_md = "mock_md" @@ -75,4 +78,4 @@ def test_gui_updates_on_event(app_instance: App) -> None: app_instance._process_pending_gui_tasks() # ANTI-SIMPLIFICATION: This assertion proves that the event pipeline # successfully transmitted state from the background thread to the GUI state. - assert app_instance._token_stats["percentage"] == 50.0 + assert app_instance._token_stats["percentage"] == 50.0 \ No newline at end of file diff --git a/tests/test_headless_simulation.py b/tests/test_headless_simulation.py index 0e5bc03..b29f4e5 100644 --- a/tests/test_headless_simulation.py +++ b/tests/test_headless_simulation.py @@ -5,10 +5,11 @@ from src.api_hook_client import ApiHookClient @pytest.mark.asyncio async def test_mma_track_lifecycle_simulation(): """ - This test simulates the sequence of API calls an external orchestrator - would make to manage an MMA track lifecycle via the Hook API. - It verifies that ApiHookClient correctly routes requests to the - corresponding endpoints in src/api_hooks.py. + + This test simulates the sequence of API calls an external orchestrator + would make to manage an MMA track lifecycle via the Hook API. + It verifies that ApiHookClient correctly routes requests to the + corresponding endpoints in src/api_hooks.py. """ client = ApiHookClient("http://localhost:8999") @@ -114,4 +115,4 @@ async def test_mma_track_lifecycle_simulation(): if __name__ == "__main__": import asyncio - asyncio.run(test_mma_track_lifecycle_simulation()) + asyncio.run(test_mma_track_lifecycle_simulation()) \ No newline at end of file diff --git a/tests/test_headless_verification.py b/tests/test_headless_verification.py index 813d825..ddf11aa 100644 --- a/tests/test_headless_verification.py +++ b/tests/test_headless_verification.py @@ -9,11 +9,12 @@ from src import ai_client @pytest.mark.asyncio async def test_headless_verification_full_run(vlogger) -> None: """ - 1. Initialize a ConductorEngine with a Track containing multiple dependent Tickets. - 2. Simulate a full execution run using engine.run(). - 3. Mock ai_client.send to simulate successful tool calls and final responses. - 4. Specifically verify that 'Context Amnesia' is maintained. - """ + + 1. Initialize a ConductorEngine with a Track containing multiple dependent Tickets. + 2. Simulate a full execution run using engine.run(). + 3. Mock ai_client.send to simulate successful tool calls and final responses. + 4. Specifically verify that 'Context Amnesia' is maintained. + """ t1 = Ticket(id="T1", description="Task 1", status="todo", assigned_to="worker1") t2 = Ticket(id="T2", description="Task 2", status="todo", assigned_to="worker1", depends_on=["T1"]) track = Track(id="track_verify", description="Verification Track", tickets=[t1, t2]) @@ -47,9 +48,10 @@ async def test_headless_verification_full_run(vlogger) -> None: @pytest.mark.asyncio async def test_headless_verification_error_and_qa_interceptor(vlogger) -> None: """ - 5. Simulate a shell error and verify that the Tier 4 QA interceptor is triggered - and its summary is injected into the worker's history for the next retry. - """ + + 5. Simulate a shell error and verify that the Tier 4 QA interceptor is triggered + and its summary is injected into the worker's history for the next retry. + """ t1 = Ticket(id="T1", description="Task with error", status="todo", assigned_to="worker1") track = Track(id="track_error", description="Error Track", tickets=[t1]) from src.events import SyncEventQueue @@ -140,4 +142,4 @@ async def test_headless_verification_error_and_qa_interceptor(vlogger) -> None: if "QA ANALYSIS:" in part_str and "FIX: Check if path exists." in part_str: found_qa = True assert found_qa, "QA Analysis was not injected into the next round" - vlogger.finalize("Tier 4 QA Injection", "PASS", "QA summary injected into next worker round.") + vlogger.finalize("Tier 4 QA Injection", "PASS", "QA summary injected into next worker round.") \ No newline at end of file diff --git a/tests/test_history_management.py b/tests/test_history_management.py index 4c40402..8dd234a 100644 --- a/tests/test_history_management.py +++ b/tests/test_history_management.py @@ -130,4 +130,4 @@ def test_get_history_bleed_stats_basic() -> None: assert "current" in stats, "Stats dictionary should contain 'current' token usage" assert 'limit' in stats, "Stats dictionary should contain 'limit'" assert stats['limit'] == 500 - assert isinstance(stats['current'], int) and stats['current'] >= 0 + assert isinstance(stats['current'], int) and stats['current'] >= 0 \ No newline at end of file diff --git a/tests/test_hooks.py b/tests/test_hooks.py index 0a387af..3d7e75c 100644 --- a/tests/test_hooks.py +++ b/tests/test_hooks.py @@ -54,4 +54,4 @@ def test_live_hook_server_responses(live_gui) -> None: # 4. Performance # diagnostics are available via get_gui_diagnostics or get_gui_state perf = client.get_gui_diagnostics() if hasattr(client, 'get_gui_diagnostics') else client.get_gui_state() - assert "fps" in perf or "thinking" in perf + assert "fps" in perf or "thinking" in perf \ No newline at end of file diff --git a/tests/test_layout_reorganization.py b/tests/test_layout_reorganization.py index 9246e5e..10cb625 100644 --- a/tests/test_layout_reorganization.py +++ b/tests/test_layout_reorganization.py @@ -11,8 +11,9 @@ from src.gui_2 import App def test_new_hubs_defined_in_show_windows(mock_app: App) -> None: """ - Verifies that the new consolidated Hub windows are defined in the App's show_windows. - This ensures they will be available in the 'Windows' menu. + + Verifies that the new consolidated Hub windows are defined in the App's show_windows. + This ensures they will be available in the 'Windows' menu. """ expected_hubs = [ "Project Settings", @@ -25,7 +26,8 @@ def test_new_hubs_defined_in_show_windows(mock_app: App) -> None: def test_old_windows_removed_from_gui2(app_instance_simple: Any) -> None: """ - Verifies that the old fragmented windows are removed or renamed. + + Verifies that the old fragmented windows are removed or renamed. """ old_tags = [ "win_projects", "win_files", "win_screenshots", @@ -51,7 +53,8 @@ def app_instance_simple() -> Any: def test_hub_windows_exist_in_gui2(app_instance_simple: Any) -> None: """ - Verifies that the new Hub windows are present in the show_windows dictionary. + + Verifies that the new Hub windows are present in the show_windows dictionary. """ hubs = ["Project Settings", "AI Settings", "Discussion Hub", "Operations Hub"] for hub in hubs: @@ -59,7 +62,8 @@ def test_hub_windows_exist_in_gui2(app_instance_simple: Any) -> None: def test_indicators_logic_exists(app_instance_simple: Any) -> None: """ - Verifies that the status indicators logic exists in the App. + + Verifies that the status indicators logic exists in the App. """ assert hasattr(app_instance_simple, 'ai_status') - assert hasattr(app_instance_simple, 'mma_status') + assert hasattr(app_instance_simple, 'mma_status') \ No newline at end of file diff --git a/tests/test_live_gui_integration_v2.py b/tests/test_live_gui_integration_v2.py index 38768c9..b408913 100644 --- a/tests/test_live_gui_integration_v2.py +++ b/tests/test_live_gui_integration_v2.py @@ -13,11 +13,12 @@ from src.api_hook_client import ApiHookClient @pytest.mark.timeout(10) def test_user_request_integration_flow(mock_app: App) -> None: """ - Verifies that pushing a UserRequestEvent to the event_queue: - 1. Triggers ai_client.send - 2. Results in a 'response' event back to the queue - 3. Eventually updates the UI state (ai_response, ai_status) after processing GUI tasks. - ANTI-SIMPLIFICATION: This verifies the full cross-thread boundary. + + Verifies that pushing a UserRequestEvent to the event_queue: + 1. Triggers ai_client.send + 2. Results in a 'response' event back to the queue + 3. Eventually updates the UI state (ai_response, ai_status) after processing GUI tasks. + ANTI-SIMPLIFICATION: This verifies the full cross-thread boundary. """ app = mock_app # Mock all ai_client methods called during _handle_request_event @@ -74,8 +75,9 @@ def test_user_request_integration_flow(mock_app: App) -> None: @pytest.mark.timeout(10) def test_user_request_error_handling(mock_app: App) -> None: """ - Verifies that if ai_client.send raises an exception, the UI is updated with the error state. - """ + + Verifies that if ai_client.send raises an exception, the UI is updated with the error state. + """ app = mock_app with ( patch('src.ai_client.send', side_effect=Exception("API Failure")), @@ -129,4 +131,4 @@ def test_api_gui_state_live(live_gui) -> None: assert success, f"GUI state did not update. Got: {client.get_gui_state()}" final_state = client.get_gui_state() assert final_state['current_provider'] == 'anthropic' - assert final_state['current_model'] == 'claude-3-haiku-20240307' + assert final_state['current_model'] == 'claude-3-haiku-20240307' \ No newline at end of file diff --git a/tests/test_live_workflow.py b/tests/test_live_workflow.py index c638848..c2e1202 100644 --- a/tests/test_live_workflow.py +++ b/tests/test_live_workflow.py @@ -15,7 +15,8 @@ from src.api_hook_client import ApiHookClient def wait_for_value(client, field, expected, timeout=10): """ - Helper to poll the GUI state until a field matches the expected value. + + Helper to poll the GUI state until a field matches the expected value. """ start = time.time() while time.time() - start < timeout: @@ -29,9 +30,10 @@ def wait_for_value(client, field, expected, timeout=10): @pytest.mark.integration def test_full_live_workflow(live_gui) -> None: """ - Integration test that drives the GUI through a full workflow. - ANTI-SIMPLIFICATION: Asserts exact AI behavior, thinking state tracking, - and response logging in discussion history. + + Integration test that drives the GUI through a full workflow. + ANTI-SIMPLIFICATION: Asserts exact AI behavior, thinking state tracking, + and response logging in discussion history. """ client = ApiHookClient() assert client.wait_for_server(timeout=10) @@ -150,4 +152,4 @@ def test_full_live_workflow(live_gui) -> None: entries = session.get('session', {}).get('entries', []) print(f" New discussion history length: {len(entries)}") assert len(entries) == 0 - print("[TEST] Workflow completed successfully.") + print("[TEST] Workflow completed successfully.") \ No newline at end of file diff --git a/tests/test_log_registry.py b/tests/test_log_registry.py index f940114..327886b 100644 --- a/tests/test_log_registry.py +++ b/tests/test_log_registry.py @@ -151,4 +151,4 @@ class TestLogRegistry(unittest.TestCase): self.assertIn(session_id_old_nw, all_found_session_ids) self.assertIn(session_id_old_nw_incomplete, all_found_session_ids) self.assertIn(session_id_recent_nw, all_found_session_ids) - self.assertNotIn(session_id_old_w, all_found_session_ids) + self.assertNotIn(session_id_old_w, all_found_session_ids) \ No newline at end of file diff --git a/tests/test_mma_agent_focus_phase1.py b/tests/test_mma_agent_focus_phase1.py index 7c608ed..b63963f 100644 --- a/tests/test_mma_agent_focus_phase1.py +++ b/tests/test_mma_agent_focus_phase1.py @@ -82,4 +82,4 @@ def test_append_tool_log_dict_keys(app_instance) -> None: assert key in entry, f"key '{key}' missing from tool log entry: {entry}" assert entry["script"] == "pwd" assert entry["result"] == "/projects" - assert entry["source_tier"] is None + assert entry["source_tier"] is None \ No newline at end of file diff --git a/tests/test_mma_agent_focus_phase3.py b/tests/test_mma_agent_focus_phase3.py index 488ed0c..0307117 100644 --- a/tests/test_mma_agent_focus_phase3.py +++ b/tests/test_mma_agent_focus_phase3.py @@ -86,4 +86,4 @@ def test_comms_log_filter_not_applied_for_prior_session(app_instance): log_to_render = app.prior_session_entries if app.is_viewing_prior_session else list(app._comms_log) if app.ui_focus_agent and not app.is_viewing_prior_session: log_to_render = [e for e in log_to_render if e.get("source_tier") == app.ui_focus_agent] - assert len(log_to_render) == 2 + assert len(log_to_render) == 2 \ No newline at end of file diff --git a/tests/test_mma_approval_indicators.py b/tests/test_mma_approval_indicators.py index ad66b37..0d80a84 100644 --- a/tests/test_mma_approval_indicators.py +++ b/tests/test_mma_approval_indicators.py @@ -5,6 +5,9 @@ from src.gui_2 import App def _make_app(**kwargs): + """ + [C: tests/test_mma_dashboard_streams.py:TestMMADashboardStreams.test_tier1_renders_stream_content, tests/test_mma_dashboard_streams.py:TestMMADashboardStreams.test_tier3_renders_worker_subheaders] + """ app = MagicMock() app.mma_streams = kwargs.get("mma_streams", {}) app.mma_tier_usage = kwargs.get("mma_tier_usage", { @@ -61,6 +64,9 @@ def _make_app(**kwargs): return app def _make_imgui_mock(): + """ + [C: tests/test_mma_dashboard_streams.py:TestMMADashboardStreams.test_tier1_renders_stream_content, tests/test_mma_dashboard_streams.py:TestMMADashboardStreams.test_tier3_renders_worker_subheaders] + """ m = MagicMock() m.begin_table.return_value = False m.begin_child.return_value = False @@ -136,4 +142,4 @@ class TestMMAApprovalIndicators: combined = _collect_text_colored_args(imgui_mock) assert "APPROVAL PENDING" in combined, ( "text_colored not called with 'APPROVAL PENDING' when _pending_ask_dialog is True" - ) + ) \ No newline at end of file diff --git a/tests/test_mma_concurrent_tracks_sim.py b/tests/test_mma_concurrent_tracks_sim.py index b86850a..6a6fbf7 100644 --- a/tests/test_mma_concurrent_tracks_sim.py +++ b/tests/test_mma_concurrent_tracks_sim.py @@ -10,7 +10,10 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "s from src import api_hook_client def _poll_mma_status(client, timeout, condition, label): - """Poll get_mma_status() until condition(status) is True or timeout.""" + """ + Poll get_mma_status() until condition(status) is True or timeout. + [C: tests/test_mma_step_mode_sim.py:test_mma_step_mode_approval_flow] + """ last_status = {} for i in range(timeout): status = client.get_mma_status() or {} @@ -24,9 +27,10 @@ def _poll_mma_status(client, timeout, condition, label): @pytest.mark.timeout(300) def test_mma_concurrent_tracks_execution(live_gui) -> None: """ - Stress test for concurrent MMA track execution. - Verifies that starting multiple tracks simultaneously doesn't cause crashes - and that workers from both tracks are processed. + + Stress test for concurrent MMA track execution. + Verifies that starting multiple tracks simultaneously doesn't cause crashes + and that workers from both tracks are processed. """ client = api_hook_client.ApiHookClient() assert client.wait_for_server(timeout=15), "Hook server did not start" @@ -132,4 +136,4 @@ def test_mma_concurrent_tracks_execution(live_gui) -> None: assert status is not None assert status.get('mma_status') in ['done', 'idle', 'running'] - print("[SIM] Concurrent MMA tracks stress test PASSED.") + print("[SIM] Concurrent MMA tracks stress test PASSED.") \ No newline at end of file diff --git a/tests/test_mma_concurrent_tracks_stress_sim.py b/tests/test_mma_concurrent_tracks_stress_sim.py index 718ede0..48fc7a9 100644 --- a/tests/test_mma_concurrent_tracks_stress_sim.py +++ b/tests/test_mma_concurrent_tracks_stress_sim.py @@ -24,9 +24,10 @@ def _poll_mma_workers(client: api_hook_client.ApiHookClient, timeout: int, condi @pytest.mark.timeout(600) def test_mma_concurrent_tracks_stress(live_gui) -> None: """ - Stress test: Start two tracks concurrently and verify they both progress - without crashing the GUI or losing state. - """ + + Stress test: Start two tracks concurrently and verify they both progress + without crashing the GUI or losing state. + """ client = api_hook_client.ApiHookClient() assert client.wait_for_server(timeout=15), "Hook server did not start" @@ -95,4 +96,4 @@ def test_mma_concurrent_tracks_stress(live_gui) -> None: res = client.get_status() assert res.get('status') == 'ok', "GUI crashed during concurrent execution" - print("[SIM] MMA Concurrent Tracks stress test PASSED.") + print("[SIM] MMA Concurrent Tracks stress test PASSED.") \ No newline at end of file diff --git a/tests/test_mma_dashboard_streams.py b/tests/test_mma_dashboard_streams.py index 0cbf756..d36ddf6 100644 --- a/tests/test_mma_dashboard_streams.py +++ b/tests/test_mma_dashboard_streams.py @@ -81,4 +81,4 @@ class TestMMADashboardStreams: App._render_tier_stream_panel(app, "Tier 3", None) text_args = " ".join(str(c) for c in imgui_mock.text.call_args_list) assert "T-001" in text_args, "imgui.text not called with 'T-001' worker sub-header" - assert "T-002" in text_args, "imgui.text not called with 'T-002' worker sub-header" + assert "T-002" in text_args, "imgui.text not called with 'T-002' worker sub-header" \ No newline at end of file diff --git a/tests/test_mma_models.py b/tests/test_mma_models.py index ba01ca7..2e44c7d 100644 --- a/tests/test_mma_models.py +++ b/tests/test_mma_models.py @@ -2,9 +2,10 @@ from src.models import Ticket, Track, WorkerContext def test_ticket_instantiation() -> None: """ - Verifies that a Ticket can be instantiated with its required fields: - id, description, status, assigned_to. - """ + + Verifies that a Ticket can be instantiated with its required fields: + id, description, status, assigned_to. + """ ticket_id = "T1" description = "Implement surgical code changes" status = "todo" @@ -23,8 +24,9 @@ def test_ticket_instantiation() -> None: def test_ticket_with_dependencies() -> None: """ - Verifies that a Ticket can store dependencies. - """ + + Verifies that a Ticket can store dependencies. + """ ticket = Ticket( id="T2", description="Write code", @@ -36,9 +38,10 @@ def test_ticket_with_dependencies() -> None: def test_track_instantiation() -> None: """ - Verifies that a Track can be instantiated with its required fields: - id, description, and a list of Tickets. - """ + + Verifies that a Track can be instantiated with its required fields: + id, description, and a list of Tickets. + """ ticket1 = Ticket(id="T1", description="Task 1", status="todo", assigned_to="a") ticket2 = Ticket(id="T2", description="Task 2", status="todo", assigned_to="b") track_id = "TRACK-1" @@ -57,16 +60,18 @@ def test_track_instantiation() -> None: def test_track_can_handle_empty_tickets() -> None: """ - Verifies that a Track can be instantiated with an empty list of tickets. - """ + + Verifies that a Track can be instantiated with an empty list of tickets. + """ track = Track(id="TRACK-2", description="Empty Track", tickets=[]) assert track.tickets == [] def test_worker_context_instantiation() -> None: """ - Verifies that a WorkerContext can be instantiated with ticket_id, - model_name, and messages. - """ + + Verifies that a WorkerContext can be instantiated with ticket_id, + model_name, and messages. + """ ticket_id = "T1" model_name = "gemini-2.0-flash-lite" messages = [ @@ -84,26 +89,29 @@ def test_worker_context_instantiation() -> None: def test_ticket_mark_blocked() -> None: """ - Verifies that ticket.mark_blocked(reason) sets the status to 'blocked'. - Note: The reason field might need to be added to the Ticket class. - """ + + Verifies that ticket.mark_blocked(reason) sets the status to 'blocked'. + Note: The reason field might need to be added to the Ticket class. + """ ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="a") ticket.mark_blocked("Waiting for API key") assert ticket.status == "blocked" def test_ticket_mark_complete() -> None: """ - Verifies that ticket.mark_complete() sets the status to 'completed'. - """ + + Verifies that ticket.mark_complete() sets the status to 'completed'. + """ ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="a") ticket.mark_complete() assert ticket.status == "completed" def test_track_get_executable_tickets() -> None: """ - Verifies that track.get_executable_tickets() returns only 'todo' tickets - whose dependencies are all 'completed'. - """ + + Verifies that track.get_executable_tickets() returns only 'todo' tickets + whose dependencies are all 'completed'. + """ # T1: todo, no deps -> executable t1 = Ticket(id="T1", description="T1", status="todo", assigned_to="a") # T2: todo, deps [T1] -> not executable (T1 is todo) @@ -125,11 +133,12 @@ def test_track_get_executable_tickets() -> None: def test_track_get_executable_tickets_complex() -> None: """ - Verifies executable tickets with complex dependency chains. - Chain: T1 (comp) -> T2 (todo) -> T3 (todo) - T4 (comp) -> T3 - T5 (todo) -> T3 - """ + + Verifies executable tickets with complex dependency chains. + Chain: T1 (comp) -> T2 (todo) -> T3 (todo) + T4 (comp) -> T3 + T5 (todo) -> T3 + """ t1 = Ticket(id="T1", description="T1", status="completed", assigned_to="a") t2 = Ticket(id="T2", description="T2", status="todo", assigned_to="a", depends_on=["T1"]) t3 = Ticket(id="T3", description="T3", status="todo", assigned_to="a", depends_on=["T2", "T4", "T5"]) @@ -156,4 +165,4 @@ def test_track_get_executable_tickets_complex() -> None: # Now T3 should be executable executable = track.get_executable_tickets() executable_ids = sorted([t.id for t in executable]) - assert executable_ids == ["T3"] + assert executable_ids == ["T3"] \ No newline at end of file diff --git a/tests/test_mma_node_editor.py b/tests/test_mma_node_editor.py index de3dbd8..60c8cc6 100644 --- a/tests/test_mma_node_editor.py +++ b/tests/test_mma_node_editor.py @@ -48,4 +48,4 @@ def test_link_id_stability(): link_id = abs(hash(source_tid + "_" + target_tid)) assert link_id == abs(hash(source_tid + "_" + target_tid)) - assert link_id != abs(hash(target_tid + "_" + source_tid)) + assert link_id != abs(hash(target_tid + "_" + source_tid)) \ No newline at end of file diff --git a/tests/test_mma_orchestration_gui.py b/tests/test_mma_orchestration_gui.py index bb058b9..b089c7b 100644 --- a/tests/test_mma_orchestration_gui.py +++ b/tests/test_mma_orchestration_gui.py @@ -111,4 +111,4 @@ def test_handle_ai_response_fallback(app_instance: App) -> None: app_instance._process_pending_gui_tasks() assert app_instance.ai_response == "Regular AI Response" assert app_instance.ai_status == "done" - assert len(app_instance.mma_streams) == 0 + assert len(app_instance.mma_streams) == 0 \ No newline at end of file diff --git a/tests/test_mma_step_mode_sim.py b/tests/test_mma_step_mode_sim.py index f91854c..aff5902 100644 --- a/tests/test_mma_step_mode_sim.py +++ b/tests/test_mma_step_mode_sim.py @@ -23,8 +23,9 @@ def _poll_mma_status(client: api_hook_client.ApiHookClient, timeout: int, condit @pytest.mark.timeout(300) def test_mma_step_mode_approval_flow(live_gui) -> None: """ - Verify that we can manually approve a ticket in Step Mode and it proceeds. - """ + + Verify that we can manually approve a ticket in Step Mode and it proceeds. + """ client = api_hook_client.ApiHookClient() assert client.wait_for_server(timeout=15), "Hook server did not start" @@ -66,4 +67,4 @@ def test_mma_step_mode_approval_flow(live_gui) -> None: condition=lambda s: any(t['id'] == tid and t['status'] == 'in_progress' for t in s.get('active_tickets', []))) assert ok, "Ticket did not move to in_progress after manual approval/mutation" - print("[SIM] MMA Step Mode approval flow test PASSED.") + print("[SIM] MMA Step Mode approval flow test PASSED.") \ No newline at end of file diff --git a/tests/test_patch_modal_gui.py b/tests/test_patch_modal_gui.py index dea21eb..bfe537e 100644 --- a/tests/test_patch_modal_gui.py +++ b/tests/test_patch_modal_gui.py @@ -12,9 +12,10 @@ from src import api_hook_client @pytest.mark.timeout(120) def test_patch_modal_appears_on_trigger(live_gui) -> None: """ - Test that triggering a patch shows the modal in the GUI. - Uses live_gui fixture to start the GUI with test hooks enabled. - """ + + Test that triggering a patch shows the modal in the GUI. + Uses live_gui fixture to start the GUI with test hooks enabled. + """ proc, _ = live_gui client = api_hook_client.ApiHookClient() @@ -49,8 +50,9 @@ def test_patch_modal_appears_on_trigger(live_gui) -> None: @pytest.mark.timeout(120) def test_patch_apply_modal_workflow(live_gui) -> None: """ - Test the full patch apply workflow: trigger -> apply -> verify modal closes. - """ + + Test the full patch apply workflow: trigger -> apply -> verify modal closes. + """ proc, _ = live_gui client = api_hook_client.ApiHookClient() @@ -77,4 +79,4 @@ def test_patch_apply_modal_workflow(live_gui) -> None: time.sleep(1) status = client.get_gui_state() - assert status.get("_show_patch_modal") == False, "Patch modal should close after hide" + assert status.get("_show_patch_modal") == False, "Patch modal should close after hide" \ No newline at end of file diff --git a/tests/test_preset_manager.py b/tests/test_preset_manager.py index 8818999..0579f89 100644 --- a/tests/test_preset_manager.py +++ b/tests/test_preset_manager.py @@ -125,4 +125,4 @@ system_prompt = "both_p" pm.delete_preset("project1", scope="project") presets = pm.load_all() assert "project1" not in presets - assert "both" in presets # still in global + assert "both" in presets # still in global \ No newline at end of file diff --git a/tests/test_preset_windows_layout.py b/tests/test_preset_windows_layout.py index f138584..09b7a70 100644 --- a/tests/test_preset_windows_layout.py +++ b/tests/test_preset_windows_layout.py @@ -43,4 +43,4 @@ def test_api_hook_under_load(live_gui): for res in results: assert res is not None - assert res.get("status") == "ok" + assert res.get("status") == "ok" \ No newline at end of file diff --git a/tests/test_presets.py b/tests/test_presets.py index 6824359..4bb663e 100644 --- a/tests/test_presets.py +++ b/tests/test_presets.py @@ -74,4 +74,4 @@ class TestPresetManager(unittest.TestCase): self.assertEqual(manager.project_path, new_root / "project_presets.toml") if __name__ == "__main__": - unittest.main() + unittest.main() \ No newline at end of file diff --git a/tests/test_project_serialization.py b/tests/test_project_serialization.py index a9ceb71..b1a7834 100644 --- a/tests/test_project_serialization.py +++ b/tests/test_project_serialization.py @@ -87,4 +87,4 @@ roles = ["User", "AI"] self.assertIn("Context", proj["discussion"]["roles"]) if __name__ == "__main__": - unittest.main() + unittest.main() \ No newline at end of file diff --git a/tests/test_provider_curation.py b/tests/test_provider_curation.py index f6d7b39..319c9cc 100644 --- a/tests/test_provider_curation.py +++ b/tests/test_provider_curation.py @@ -5,4 +5,4 @@ def test_providers_moved_to_models(): """Verify that PROVIDERS list is in models.py and removed from AppController.""" expected_providers = ['gemini', 'anthropic', 'gemini_cli', 'deepseek', 'minimax'] assert models.PROVIDERS == expected_providers - assert not hasattr(src.app_controller.AppController, 'PROVIDERS') + assert not hasattr(src.app_controller.AppController, 'PROVIDERS') \ No newline at end of file diff --git a/tests/test_rag_gui_presence.py b/tests/test_rag_gui_presence.py index 7be4c99..5d29a90 100644 --- a/tests/test_rag_gui_presence.py +++ b/tests/test_rag_gui_presence.py @@ -12,4 +12,4 @@ def test_rag_panel_integration(): import inspect source = inspect.getsource(App._gui_func) assert "self._render_rag_panel()" in source - assert "imgui.collapsing_header(\"RAG Settings\")" in source + assert "imgui.collapsing_header(\"RAG Settings\")" in source \ No newline at end of file diff --git a/tests/test_rag_integration.py b/tests/test_rag_integration.py index cd91618..27eb24c 100644 --- a/tests/test_rag_integration.py +++ b/tests/test_rag_integration.py @@ -24,7 +24,8 @@ def mock_project(): def test_rag_integration(mock_project): """ - Integration test verifying the flow from AppController through RAGEngine to ai_client. + + Integration test verifying the flow from AppController through RAGEngine to ai_client. """ # 1. Initializes a mock project and AppController. # We patch several components to avoid side effects during initialization. @@ -108,4 +109,4 @@ def test_rag_integration(mock_project): assert "Source: test_file.py" in sent_user_message # Verify that rag_engine.search was called with the original prompt - mock_rag_engine.search.assert_called_once_with("Tell me about the code.") + mock_rag_engine.search.assert_called_once_with("Tell me about the code.") \ No newline at end of file diff --git a/tests/test_run_worker_lifecycle_abort.py b/tests/test_run_worker_lifecycle_abort.py index a45ef81..bc99173 100644 --- a/tests/test_run_worker_lifecycle_abort.py +++ b/tests/test_run_worker_lifecycle_abort.py @@ -9,8 +9,9 @@ from src.models import Ticket, WorkerContext class TestRunWorkerLifecycleAbort(unittest.TestCase): def test_run_worker_lifecycle_returns_early_on_abort(self): """ - Test that run_worker_lifecycle returns early and marks ticket as 'killed' - if the abort event is set for the ticket. + + Test that run_worker_lifecycle returns early and marks ticket as 'killed' + if the abort event is set for the ticket. """ # Mock ai_client.send with patch('src.ai_client.send') as mock_send: @@ -37,4 +38,4 @@ class TestRunWorkerLifecycleAbort(unittest.TestCase): mock_send.assert_not_called() if __name__ == "__main__": - unittest.main() + unittest.main() \ No newline at end of file diff --git a/tests/test_selectable_ui.py b/tests/test_selectable_ui.py index 692891a..1d30ca0 100644 --- a/tests/test_selectable_ui.py +++ b/tests/test_selectable_ui.py @@ -12,8 +12,9 @@ from src.gui_2 import App @pytest.mark.integration def test_selectable_label_stability(live_gui) -> None: """ - Verifies that the application starts correctly with --enable-test-hooks - and that the selectable label infrastructure is present and stable. + + Verifies that the application starts correctly with --enable-test-hooks + and that the selectable label infrastructure is present and stable. """ client = ApiHookClient() assert client.wait_for_server(timeout=20), "Hook server failed to start" @@ -44,4 +45,4 @@ def test_selectable_label_stability(live_gui) -> None: # 5. Verify prior session indicator specifically via the gettable field # prior_session_indicator is mapped to AppController.is_viewing_prior_session prior_val = client.get_value("prior_session_indicator") - assert prior_val is False, "prior_session_indicator field should be False initially" + assert prior_val is False, "prior_session_indicator field should be False initially" \ No newline at end of file diff --git a/tests/test_sim_ai_settings.py b/tests/test_sim_ai_settings.py index 868aede..a2699b5 100644 --- a/tests/test_sim_ai_settings.py +++ b/tests/test_sim_ai_settings.py @@ -15,8 +15,9 @@ from simulation.sim_ai_settings import AISettingsSimulation def test_ai_settings_simulation_run() -> None: """ - Verifies that AISettingsSimulation correctly cycles through models - to test the settings UI components. + + Verifies that AISettingsSimulation correctly cycles through models + to test the settings UI components. """ mock_client = MagicMock() mock_client.wait_for_server.return_value = True @@ -42,4 +43,4 @@ def test_ai_settings_simulation_run() -> None: # Verify calls # ANTI-SIMPLIFICATION: Assert that specific models were set during simulation mock_client.set_value.assert_any_call("current_model", "gemini-2.0-flash") - mock_client.set_value.assert_any_call("current_model", "gemini-2.5-flash-lite") + mock_client.set_value.assert_any_call("current_model", "gemini-2.5-flash-lite") \ No newline at end of file diff --git a/tests/test_sim_base.py b/tests/test_sim_base.py index 4bf125b..017236d 100644 --- a/tests/test_sim_base.py +++ b/tests/test_sim_base.py @@ -15,7 +15,8 @@ from simulation.sim_base import BaseSimulation def test_base_simulation_init() -> None: """ - Verifies that the BaseSimulation initializes the ApiHookClient correctly. + + Verifies that the BaseSimulation initializes the ApiHookClient correctly. """ with patch('simulation.sim_base.ApiHookClient') as mock_client_class: mock_client = MagicMock() @@ -27,8 +28,9 @@ def test_base_simulation_init() -> None: def test_base_simulation_setup() -> None: """ - Verifies that the setup routine correctly resets the GUI state - and initializes a clean temporary project for simulation. + + Verifies that the setup routine correctly resets the GUI state + and initializes a clean temporary project for simulation. """ mock_client = MagicMock() mock_client.wait_for_server.return_value = True @@ -43,4 +45,4 @@ def test_base_simulation_setup() -> None: mock_client.click.assert_any_call("btn_reset") mock_sim.setup_new_project.assert_called() from pathlib import Path - assert Path(sim.project_path).as_posix().endswith("tests/artifacts/temp_testsim.toml") + assert Path(sim.project_path).as_posix().endswith("tests/artifacts/temp_testsim.toml") \ No newline at end of file diff --git a/tests/test_sim_context.py b/tests/test_sim_context.py index 4a33586..d728b56 100644 --- a/tests/test_sim_context.py +++ b/tests/test_sim_context.py @@ -15,8 +15,9 @@ from simulation.sim_context import ContextSimulation def test_context_simulation_run() -> None: """ - Verifies that the ContextSimulation runs the correct sequence of user actions: - discussion switching, context building (md_only), and history truncation. + + Verifies that the ContextSimulation runs the correct sequence of user actions: + discussion switching, context building (md_only), and history truncation. """ mock_client = MagicMock() mock_client.wait_for_server.return_value = True @@ -52,4 +53,4 @@ def test_context_simulation_run() -> None: mock_client.post_project.assert_called() mock_client.click.assert_called_with("btn_md_only") mock_sim.run_discussion_turn.assert_called() - mock_sim.truncate_history.assert_called_with(1) + mock_sim.truncate_history.assert_called_with(1) \ No newline at end of file diff --git a/tests/test_sim_execution.py b/tests/test_sim_execution.py index ffe5d22..4c04672 100644 --- a/tests/test_sim_execution.py +++ b/tests/test_sim_execution.py @@ -15,8 +15,9 @@ from simulation.sim_execution import ExecutionSimulation def test_execution_simulation_run() -> None: """ - Verifies that ExecutionSimulation handles script confirmation modals. - Ensures that it waits for the modal and clicks the approve button. + + Verifies that ExecutionSimulation handles script confirmation modals. + Ensures that it waits for the modal and clicks the approve button. """ mock_client = MagicMock() mock_client.wait_for_server.return_value = True @@ -52,4 +53,4 @@ def test_execution_simulation_run() -> None: # Verify calls # ANTI-SIMPLIFICATION: Assert that the async discussion and the script approval button are triggered. mock_sim.run_discussion_turn_async.assert_called() - mock_client.click.assert_called_with("btn_approve_script") + mock_client.click.assert_called_with("btn_approve_script") \ No newline at end of file diff --git a/tests/test_sim_tools.py b/tests/test_sim_tools.py index 296b808..17f38d0 100644 --- a/tests/test_sim_tools.py +++ b/tests/test_sim_tools.py @@ -15,8 +15,9 @@ from simulation.sim_tools import ToolsSimulation def test_tools_simulation_run() -> None: """ - Verifies that ToolsSimulation requests specific tool executions - and verifies they appear in the resulting session history. + + Verifies that ToolsSimulation requests specific tool executions + and verifies they appear in the resulting session history. """ mock_client = MagicMock() mock_client.wait_for_server.return_value = True @@ -39,4 +40,4 @@ def test_tools_simulation_run() -> None: # Verify calls # ANTI-SIMPLIFICATION: Must assert the specific commands were tested mock_sim.run_discussion_turn.assert_any_call("List the files in the current directory.") - mock_sim.run_discussion_turn.assert_any_call("Read the first 10 lines of aggregate.py.") + mock_sim.run_discussion_turn.assert_any_call("Read the first 10 lines of aggregate.py.") \ No newline at end of file diff --git a/tests/test_sync_events.py b/tests/test_sync_events.py index 7765c3f..fc3b90c 100644 --- a/tests/test_sync_events.py +++ b/tests/test_sync_events.py @@ -28,4 +28,4 @@ def test_sync_event_queue_none_payload() -> None: queue.put("no_payload") name, payload = queue.get() assert name == "no_payload" - assert payload is None + assert payload is None \ No newline at end of file diff --git a/tests/test_sync_hooks.py b/tests/test_sync_hooks.py index beb87fa..b4318ed 100644 --- a/tests/test_sync_hooks.py +++ b/tests/test_sync_hooks.py @@ -32,4 +32,4 @@ def test_api_ask_client_error() -> None: with patch.object(client, '_make_request') as mock_make: mock_make.return_value = None result = client.request_confirmation("run_powershell", {}) - assert result is None + assert result is None \ No newline at end of file diff --git a/tests/test_system_prompt_sim.py b/tests/test_system_prompt_sim.py index e61570e..513df3b 100644 --- a/tests/test_system_prompt_sim.py +++ b/tests/test_system_prompt_sim.py @@ -11,13 +11,14 @@ from src import ai_client def test_system_prompt_sim(live_gui): """ - Simulation test for system prompt settings. - 1. Wait for server. - 2. Verify initial state. - 3. Modify settings via API. - 4. Verify updates. - 5. Use 'Reset to Default' button via API. - 6. Verify restoration to default text. + + Simulation test for system prompt settings. + 1. Wait for server. + 2. Verify initial state. + 3. Modify settings via API. + 4. Verify updates. + 5. Use 'Reset to Default' button via API. + 6. Verify restoration to default text. """ _, gui_script = live_gui client = ApiHookClient() @@ -70,4 +71,4 @@ def test_system_prompt_sim(live_gui): # Close it client.set_value('show_base_prompt_diff_modal', False) - assert client.get_value('show_base_prompt_diff_modal') is False + assert client.get_value('show_base_prompt_diff_modal') is False \ No newline at end of file diff --git a/tests/test_tier4_interceptor.py b/tests/test_tier4_interceptor.py index 09f991e..03bebce 100644 --- a/tests/test_tier4_interceptor.py +++ b/tests/test_tier4_interceptor.py @@ -61,9 +61,10 @@ def test_run_powershell_optional_qa_callback() -> None: assert "EXIT CODE: 1" in result def test_end_to_end_tier4_integration(vlogger) -> None: - """1. Start a task that triggers a tool failure. - 2. Ensure Tier 4 QA analysis is run. - 3. Verify the analysis is merged into the next turn's prompt. + """ + 1. Start a task that triggers a tool failure. + 2. Ensure Tier 4 QA analysis is run. + 3. Verify the analysis is merged into the next turn's prompt. """ # Trigger a send that results in a tool failure # (In reality, the tool loop handles this) @@ -131,4 +132,4 @@ def test_gemini_provider_passes_qa_callback_to_run_script() -> None: qa_callback=qa_callback ) # Verify _run_script received the qa_callback and patch_callback - mock_run_script.assert_called_with("dir", ".", qa_callback, None) + mock_run_script.assert_called_with("dir", ".", qa_callback, None) \ No newline at end of file diff --git a/tests/test_token_viz.py b/tests/test_token_viz.py index babd313..841e110 100644 --- a/tests/test_token_viz.py +++ b/tests/test_token_viz.py @@ -93,4 +93,4 @@ def test_gemini_cache_fields_accessible() -> None: def test_anthropic_history_lock_accessible() -> None: """_anthropic_history_lock must be accessible for cache hint rendering.""" assert hasattr(ai_client, "_anthropic_history_lock") - assert hasattr(ai_client, "_anthropic_history") + assert hasattr(ai_client, "_anthropic_history") \ No newline at end of file diff --git a/tests/test_tool_preset_env.py b/tests/test_tool_preset_env.py index f940918..dbf4abb 100644 --- a/tests/test_tool_preset_env.py +++ b/tests/test_tool_preset_env.py @@ -34,4 +34,4 @@ def test_tool_preset_env_no_var(monkeypatch): except Exception: pass - mock_set_preset.assert_not_called() + mock_set_preset.assert_not_called() \ No newline at end of file diff --git a/tests/test_track_state_persistence.py b/tests/test_track_state_persistence.py index 242ebfb..c681409 100644 --- a/tests/test_track_state_persistence.py +++ b/tests/test_track_state_persistence.py @@ -7,12 +7,13 @@ from src.project_manager import save_track_state, load_track_state def test_track_state_persistence(tmp_path) -> None: """ - Tests saving and loading a TrackState object to/from a TOML file. - 1. Create a TrackState object with sample metadata, discussion, and tasks. - 2. Call save_track_state('test_track', state, base_dir). - 3. Verify that base_dir/conductor/tracks/test_track/state.toml exists. - 4. Call load_track_state('test_track', base_dir) and verify it returns an identical TrackState object. - """ + + Tests saving and loading a TrackState object to/from a TOML file. + 1. Create a TrackState object with sample metadata, discussion, and tasks. + 2. Call save_track_state('test_track', state, base_dir). + 3. Verify that base_dir/conductor/tracks/test_track/state.toml exists. + 4. Call load_track_state('test_track', base_dir) and verify it returns an identical TrackState object. + """ base_dir = tmp_path track_id = "test-track-999" # Metadata internal ID track_folder_name = "test_track" # Folder name used in persistence @@ -63,4 +64,4 @@ def test_track_state_persistence(tmp_path) -> None: assert loaded_state.discussion[i]["content"] == original_state.discussion[i]["content"] assert loaded_state.discussion[i]["ts"] == original_state.discussion[i]["ts"] # Final check: deep equality of dataclasses - assert loaded_state == original_state + assert loaded_state == original_state \ No newline at end of file diff --git a/tests/test_track_state_schema.py b/tests/test_track_state_schema.py index 7b37439..b85405f 100644 --- a/tests/test_track_state_schema.py +++ b/tests/test_track_state_schema.py @@ -156,4 +156,4 @@ def test_track_state_to_dict_with_none() -> None: assert track_dict["metadata"]["updated_at"] is None # This should be None as it's passed as None assert track_dict["discussion"][0]["ts"] is None assert track_dict["tasks"][0]["description"] == "Task None" - assert track_dict["tasks"][0]["assigned_to"] == "anon" + assert track_dict["tasks"][0]["assigned_to"] == "anon" \ No newline at end of file diff --git a/tests/test_tree_sitter_setup.py b/tests/test_tree_sitter_setup.py index cbd20a9..9306c01 100644 --- a/tests/test_tree_sitter_setup.py +++ b/tests/test_tree_sitter_setup.py @@ -3,9 +3,10 @@ from tree_sitter import Language, Parser def test_tree_sitter_python_setup() -> None: """ - Verifies that tree-sitter and tree-sitter-python are correctly installed - and can parse a simple Python function string. - """ + + Verifies that tree-sitter and tree-sitter-python are correctly installed + and can parse a simple Python function string. + """ # Initialize the Python language and parser PY_LANGUAGE = Language(tspython.language()) parser = Parser(PY_LANGUAGE) @@ -22,4 +23,4 @@ def test_tree_sitter_python_setup() -> None: if child.type == "function_definition": found_function = True break - assert found_function, "Should have found a function_definition node" + assert found_function, "Should have found a function_definition node" \ No newline at end of file diff --git a/tests/test_visual_mma.py b/tests/test_visual_mma.py index 0e6ffd4..6f55512 100644 --- a/tests/test_visual_mma.py +++ b/tests/test_visual_mma.py @@ -6,9 +6,10 @@ from src import api_hook_client @pytest.mark.live def test_visual_mma_components(live_gui): """ - Refactored visual MMA verification using the live_gui fixture. - Ensures the MMA dashboard and tickets are correctly rendered. - """ + + Refactored visual MMA verification using the live_gui fixture. + Ensures the MMA dashboard and tickets are correctly rendered. + """ # live_gui is a tuple (process, script_name) _, gui_script = live_gui print(f"Testing visual MMA components on {gui_script}...") @@ -85,4 +86,4 @@ def test_visual_mma_components(live_gui): "action": "click", "item": "btn_approve_mma_step" }) - time.sleep(0.5) + time.sleep(0.5) \ No newline at end of file diff --git a/tests/test_visual_sim_mma_v2.py b/tests/test_visual_sim_mma_v2.py index 82ef16f..0b73f5e 100644 --- a/tests/test_visual_sim_mma_v2.py +++ b/tests/test_visual_sim_mma_v2.py @@ -55,10 +55,11 @@ def _poll(client: api_hook_client.ApiHookClient, timeout: int, condition, label: @pytest.mark.timeout(300) def test_mma_complete_lifecycle(live_gui) -> None: """ - End-to-end MMA lifecycle using real Gemini API (gemini-2.5-flash-lite). - Incorporates frame-sync sleeps and explicit state-transition waits per - simulation_hardening_20260301 spec (Issues 2 & 3). - """ + + End-to-end MMA lifecycle using real Gemini API (gemini-2.5-flash-lite). + Incorporates frame-sync sleeps and explicit state-transition waits per + simulation_hardening_20260301 spec (Issues 2 & 3). + """ client = api_hook_client.ApiHookClient() assert client.wait_for_server(timeout=15), "Hook server did not start" @@ -196,4 +197,4 @@ def test_mma_complete_lifecycle(live_gui) -> None: if not ok: print("[SIM] WARNING: mma_tier_usage Tier 3 still zero after 30s — may not be wired to hook API yet") - print("[SIM] MMA complete lifecycle simulation PASSED.") + print("[SIM] MMA complete lifecycle simulation PASSED.") \ No newline at end of file diff --git a/tests/test_workspace_profiles_sim.py b/tests/test_workspace_profiles_sim.py index 13711fd..490dc14 100644 --- a/tests/test_workspace_profiles_sim.py +++ b/tests/test_workspace_profiles_sim.py @@ -13,12 +13,13 @@ from src import api_hook_client @pytest.mark.integration def test_workspace_profiles_restoration(live_gui): """ - Verifies that workspace profiles can save and restore UI state. - 1. Sets a field (ui_separate_tier1) to True. - 2. Saves a workspace profile. - 3. Resets the field to False. - 4. Loads the workspace profile. - 5. Verifies the field is restored to True. + + Verifies that workspace profiles can save and restore UI state. + 1. Sets a field (ui_separate_tier1) to True. + 2. Saves a workspace profile. + 3. Resets the field to False. + 4. Loads the workspace profile. + 5. Verifies the field is restored to True. """ client = api_hook_client.ApiHookClient() assert client.wait_for_server(timeout=20), "Hook server did not start" @@ -78,4 +79,4 @@ def test_workspace_profiles_restoration(live_gui): print(f"Restored value: {restored_value}") assert restored_value is True - print("Workspace profile restoration test PASSED.") + print("Workspace profile restoration test PASSED.") \ No newline at end of file diff --git a/tests/test_z_negative_flows.py b/tests/test_z_negative_flows.py index c61298d..bb0df9b 100644 --- a/tests/test_z_negative_flows.py +++ b/tests/test_z_negative_flows.py @@ -106,4 +106,4 @@ def test_mock_timeout(live_gui) -> None: assert "timeout" in event["payload"]["text"].lower() finally: # Cleanup - client.push_event('custom_callback', {'callback': '_set_env_var', 'args': ['MOCK_MODE', 'success']}) + client.push_event('custom_callback', {'callback': '_set_env_var', 'args': ['MOCK_MODE', 'success']}) \ No newline at end of file