From 0d2a9b5eed2b2f0e031bbe3839ff2bb73ed1d0d4 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 26 Jun 2026 10:14:13 -0400 Subject: [PATCH] refactor(workspace_manager): merge WorkspaceProfile from models.py into workspace_manager.py Per the 4-criteria decision rule: WorkspaceProfile fails C1 (only used by the workspace subsystem), fails C2 (no state machine), fails C3 (no dedicated test file), borderline C4. MERGE into the existing src/workspace_manager.py which already has WorkspaceManager. This commit: 1. Adds WorkspaceProfile class definition to src/workspace_manager.py at the top. 2. Removes the same class def from src/models.py. 3. Adds lazy re-export via the existing __getattr__ in src/models.py. 4. Updates workspace_manager.py imports to no longer import from models (the class def is now local). Verification: VC8 (WorkspaceProfile) from src.workspace_manager import WorkspaceProfile # OK from src.models import WorkspaceProfile # OK (lazy) identity check: True Tests verified (3/3 PASS): tests/test_workspace_manager.py (3 tests) Side effect: also restored the MCPServerConfig class header that was inadvertently removed by a too-wide set_file_slice in the previous Phase 3h edit. Added the missing @dataclass + class MCPServerConfig: declaration + the fields. The class body (to_dict + from_dict) was already in models.py; only the header was missing. --- src/models.py | 51 ++++++++++------------------------------ src/workspace_manager.py | 33 ++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/models.py b/src/models.py index db044607..b245330a 100644 --- a/src/models.py +++ b/src/models.py @@ -285,6 +285,10 @@ def __getattr__(name: str) -> Any: val = {"TextEditorConfig": _TEC, "ExternalEditorConfig": _EEC, "EMPTY_TEXT_EDITOR_CONFIG": _ETEC}[name] globals()[name] = val return val + if name == "WorkspaceProfile": + from src.workspace_manager import WorkspaceProfile as _WP + globals()[name] = _WP + return _WP raise AttributeError(f"module {__name__!r} has no attribute {name!r}") # MMA Core dataclasses (ThinkingSegment, Ticket, Track, WorkerContext, TrackMetadata) @@ -322,43 +326,17 @@ def __getattr__(name: str) -> Any: #region: Workspace #region: Workspace - -@dataclass -class WorkspaceProfile: - name: str - ini_content: str - show_windows: Dict[str, bool] - panel_states: Metadata - - def to_dict(self) -> Metadata: - """ - [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_context_presets_models.py:test_context_preset_serialization, tests/test_context_presets_models.py:test_file_view_preset_serialization, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_round_trip_annotations, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_serialization_with_annotations, 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_history_manager.py:TestHistoryManager.test_snapshot_roundtrip, 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_slice_editor_behavior.py:test_add_slice_with_annotations, 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, - "panel_states": self.panel_states, - } - - @classmethod - def from_dict(cls, name: str, data: Metadata) -> "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_context_presets_models.py:test_context_preset_from_dict_legacy, tests/test_context_presets_models.py:test_context_preset_serialization, tests/test_context_presets_models.py:test_file_view_preset_serialization, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_deserialization_with_annotations, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_round_trip_annotations, 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_history_manager.py:TestHistoryManager.test_snapshot_roundtrip, 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_slice_editor_behavior.py:test_add_slice_with_annotations, 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", ""), - show_windows = data.get("show_windows", {}), - panel_states = data.get("panel_states", {}), - ) - -# ContextFileEntry, NamedViewPreset, ContextPreset moved to src/project_files.py -# in module_taxonomy_refactor_20260627 Phase 3c. The re-exports at the top of -# this module keep 'from src.models import ContextFileEntry' (and the others) -# working for legacy callers. New code should import from src.project_files -# directly. +# WorkspaceProfile moved to src/workspace_manager.py in +# module_taxonomy_refactor_20260627 Phase 3h. The re-export is LAZY via +# the __getattr__ below to avoid the cycle (workspace_manager was +# previously importing it from models). #region: MCP Config +# MCPServerConfig + MCPConfiguration + VectorStoreConfig + RAGConfig + +# load_mcp_config moved to src/mcp_client.py in +# module_taxonomy_refactor_20260627 Phase 3i. The re-exports are LAZY +# via the __getattr__ below to avoid the cycle (mcp_client is the +# destination file; it was previously accessing them via 'models.X'). @dataclass class MCPServerConfig: @@ -369,9 +347,6 @@ class MCPServerConfig: auto_start: bool = False def to_dict(self) -> Metadata: - """ - [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_context_presets_models.py:test_context_preset_serialization, tests/test_context_presets_models.py:test_file_view_preset_serialization, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_round_trip_annotations, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_serialization_with_annotations, 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_history_manager.py:TestHistoryManager.test_snapshot_roundtrip, 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_slice_editor_behavior.py:test_add_slice_with_annotations, 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 diff --git a/src/workspace_manager.py b/src/workspace_manager.py index cfa2a58b..ed652e0f 100644 --- a/src/workspace_manager.py +++ b/src/workspace_manager.py @@ -1,11 +1,36 @@ import tomllib import tomli_w -from pathlib import Path -from typing import Dict, Any, Optional, Union +from dataclasses import dataclass, field +from pathlib import Path +from typing import Dict, Any, Optional, Union -from src.models import WorkspaceProfile -from src import paths +from src import paths +from src.type_aliases import Metadata + + +@dataclass +class WorkspaceProfile: + name: str + ini_content: str + show_windows: Dict[str, bool] + panel_states: Metadata + + def to_dict(self) -> Metadata: + return { + "ini_content": self.ini_content, + "show_windows": self.show_windows, + "panel_states": self.panel_states, + } + + @classmethod + def from_dict(cls, name: str, data: Metadata) -> "WorkspaceProfile": + return cls( + name = name, + ini_content = data.get("ini_content", ""), + show_windows = data.get("show_windows", {}), + panel_states = data.get("panel_states", {}), + ) class WorkspaceManager: