refactor(consumers): replace 'models.<moved_class>' with direct imports
Per post_module_taxonomy_de_cruft_20260627 Phase 2 (FR7 continued).
The previous migration commit (8f11340b) handled the
'from src.models import X' pattern (85 sites). This commit handles
the 'models.<moved_class>' attribute access pattern (44 sites in 20
files), which the __getattr__ shim previously supported.
The migration was performed by the one-time script
scripts/tier2/artifacts/post_module_taxonomy_de_cruft_20260627/migrate_models_attr.py
which:
1. For each 'models.<moved_class>' reference, replaces it with the
bare class name (e.g., 'models.MCPConfiguration' -> 'MCPConfiguration')
2. Adds the import 'from src.<destination> import <moved_class>' at
the top of the file (deduplicated if the import already exists)
3. Skips moved classes that the file already imports directly
The migration script inserts the import after the 'from __future__
import annotations' line if present; otherwise it adds the import
to the destination module's existing import block. Two files
required manual fixes because the script's regex didn't handle them:
- src/rag_engine.py: uses 'from src import models' (not 'from
src.models import X'); the class is accessed
via 'models.RAGConfig'. Replaced with a
direct 'from src.mcp_client import RAGConfig'
import and removed the 'from src import models'.
- tests/test_project_context_20260627.py: uses the parens-style
multi-line 'from src.models import (X, Y, Z)'.
Replaced with the parens-style direct import.
After this commit:
- 'models.MCPConfiguration', 'models.FileItem', 'models.Ticket', etc.
no longer work in src/ and tests/ (the AttributeError raises
because models.py no longer has the __getattr__ entries for
moved classes)
- All consumer files have direct imports of the moved classes
Total: 44 'models.<moved_class>' references rewritten across 20 files.
This commit is contained in:
+59
-53
@@ -1,4 +1,10 @@
|
||||
from __future__ import annotations
|
||||
from src.tool_presets import ToolPreset
|
||||
from src.mma import Ticket, Track, TrackState
|
||||
from src.personas import Persona
|
||||
from src.mcp_client import MCPConfiguration, RAGConfig, load_mcp_config
|
||||
from src.project_files import ContextPreset, FileItem, NamedViewPreset, Preset
|
||||
from src.tool_bias import BiasProfile
|
||||
|
||||
import copy
|
||||
import inspect
|
||||
@@ -505,13 +511,13 @@ def _handle_mma_state_update(controller: 'AppController', task: dict):
|
||||
if track_data:
|
||||
tickets = []
|
||||
for t_data in controller.active_tickets:
|
||||
if isinstance(t_data, models.Ticket):
|
||||
if isinstance(t_data, Ticket):
|
||||
tickets.append(t_data)
|
||||
else:
|
||||
if "goal" in t_data and "description" not in t_data:
|
||||
t_data["description"] = t_data["goal"]
|
||||
tickets.append(models.Ticket.from_dict(t_data))
|
||||
controller.active_track = models.Track(
|
||||
tickets.append(Ticket.from_dict(t_data))
|
||||
controller.active_track = Track(
|
||||
id=track_data.get("id"),
|
||||
description=track_data.get("title", ""),
|
||||
tickets=tickets
|
||||
@@ -998,7 +1004,7 @@ class AppController:
|
||||
self.discussion_sent_system_prompt: str = ""
|
||||
self.disc_roles: List[str] = []
|
||||
self.tracks: list[Metadata] = []
|
||||
self.active_track: Optional[models.Track] = None
|
||||
self.active_track: Optional[Track] = None
|
||||
self.engines: Dict[str, multi_agent_conductor.ConductorEngine] = {}
|
||||
self.mma_streams: Dict[str, str] = {}
|
||||
self.MAX_STREAM_SIZE: int = 10 * 1024
|
||||
@@ -1017,9 +1023,9 @@ class AppController:
|
||||
"Tier 3": {"input": 0, "output": 0, "provider": "gemini", "model": "gemini-2.5-flash-lite", "tool_preset": None},
|
||||
"Tier 4": {"input": 0, "output": 0, "provider": "gemini", "model": "gemini-2.5-flash-lite", "tool_preset": None},
|
||||
}
|
||||
self.mcp_config: models.MCPConfiguration = models.MCPConfiguration()
|
||||
self.view_presets: list[models.NamedViewPreset] = []
|
||||
self.rag_config: Optional[models.RAGConfig] = None
|
||||
self.mcp_config: MCPConfiguration = MCPConfiguration()
|
||||
self.view_presets: list[NamedViewPreset] = []
|
||||
self.rag_config: Optional[RAGConfig] = None
|
||||
self.rag_status: str = 'idle'
|
||||
self.temperature: float = 0.0
|
||||
self.top_p: float = 1.0
|
||||
@@ -1099,8 +1105,8 @@ class AppController:
|
||||
#endregion: UI State
|
||||
|
||||
# --- Media/Context ---
|
||||
self.files: List[models.FileItem] = []
|
||||
self.context_files: List[models.FileItem] = []
|
||||
self.files: List[FileItem] = []
|
||||
self.context_files: List[FileItem] = []
|
||||
self.screenshots: List[str] = []
|
||||
|
||||
# --- Services ---
|
||||
@@ -1110,7 +1116,7 @@ class AppController:
|
||||
# --- Defaults set here so tests that construct AppController without
|
||||
# calling init_state() still see the attributes ---
|
||||
self.ui_global_preset_name: Optional[str] = None
|
||||
self.active_tickets: list[models.Ticket] = []
|
||||
self.active_tickets: list[Ticket] = []
|
||||
self.ui_selected_tickets: Set[str] = set()
|
||||
|
||||
#region: --- Configuration Maps ---
|
||||
@@ -1753,7 +1759,7 @@ class AppController:
|
||||
on `self._mcp_config_parse_error` for sub-track 4 GUI."""
|
||||
try:
|
||||
data = json.loads(value)
|
||||
self.mcp_config = models.MCPConfiguration.from_dict(data)
|
||||
self.mcp_config = MCPConfiguration.from_dict(data)
|
||||
return OK
|
||||
except (json.JSONDecodeError, ValueError, TypeError, KeyError, AttributeError) as e:
|
||||
return Result(data=None, errors=[ErrorInfo(
|
||||
@@ -1778,7 +1784,7 @@ class AppController:
|
||||
new_files.append(old_files[p])
|
||||
else:
|
||||
from src import models
|
||||
new_files.append(models.FileItem(path=p, injected_at=now))
|
||||
new_files.append(FileItem(path=p, injected_at=now))
|
||||
self.files = new_files
|
||||
|
||||
@property
|
||||
@@ -1998,12 +2004,12 @@ class AppController:
|
||||
raw_paths = self.project.get("files", {}).get("paths", [])
|
||||
self.files = []
|
||||
for p in raw_paths:
|
||||
if isinstance(p, models.FileItem):
|
||||
if isinstance(p, FileItem):
|
||||
self.files.append(p)
|
||||
elif isinstance(p, dict):
|
||||
self.files.append(models.FileItem.from_dict(p))
|
||||
self.files.append(FileItem.from_dict(p))
|
||||
else:
|
||||
self.files.append(models.FileItem(path=str(p)))
|
||||
self.files.append(FileItem(path=str(p)))
|
||||
self.screenshots = list(self.project.get("screenshots", {}).get("paths", []))
|
||||
disc_sec = self.project.get("discussion", {})
|
||||
self.disc_roles = list(disc_sec.get("roles", ["User", "AI", "Vendor API", "System", "Reasoning", "Context"]))
|
||||
@@ -2040,14 +2046,14 @@ class AppController:
|
||||
mcp_p = Path(mcp_path)
|
||||
if not mcp_p.is_absolute() and self.active_project_path:
|
||||
mcp_p = Path(self.active_project_path).parent / mcp_path
|
||||
if mcp_p.exists(): self.mcp_config = models.load_mcp_config(str(mcp_p))
|
||||
else: self.mcp_config = models.MCPConfiguration()
|
||||
if mcp_p.exists(): self.mcp_config = load_mcp_config(str(mcp_p))
|
||||
else: self.mcp_config = MCPConfiguration()
|
||||
else:
|
||||
self.mcp_config = models.MCPConfiguration()
|
||||
self.mcp_config = MCPConfiguration()
|
||||
|
||||
rag_data = self.config.get('rag')
|
||||
if rag_data: self.rag_config = models.RAGConfig.from_dict(rag_data)
|
||||
else: self.rag_config = models.RAGConfig()
|
||||
if rag_data: self.rag_config = RAGConfig.from_dict(rag_data)
|
||||
else: self.rag_config = RAGConfig()
|
||||
|
||||
self.rag_engine = None
|
||||
if self.rag_config.enabled: self._sync_rag_engine()
|
||||
@@ -2145,8 +2151,8 @@ class AppController:
|
||||
try:
|
||||
tickets = []
|
||||
for t_data in at_data.get("tickets", []):
|
||||
tickets.append(models.Ticket(**t_data))
|
||||
track = models.Track(
|
||||
tickets.append(Ticket(**t_data))
|
||||
track = Track(
|
||||
id=at_data.get("id"),
|
||||
description=at_data.get("description"),
|
||||
tickets=tickets
|
||||
@@ -2543,7 +2549,7 @@ class AppController:
|
||||
file_path = os.path.relpath(file_path, self.active_project_root)
|
||||
existing = next((f for f in self.files if f.path == file_path), None)
|
||||
if not existing:
|
||||
item = models.FileItem(path=file_path)
|
||||
item = FileItem(path=file_path)
|
||||
self.files.append(item)
|
||||
self._refresh_from_project()
|
||||
|
||||
@@ -3232,19 +3238,19 @@ class AppController:
|
||||
raw_paths = self.project.get("files", {}).get("paths", [])
|
||||
self.files = []
|
||||
for p in raw_paths:
|
||||
if isinstance(p, models.FileItem):
|
||||
if isinstance(p, FileItem):
|
||||
self.files.append(p)
|
||||
elif isinstance(p, dict):
|
||||
self.files.append(models.FileItem.from_dict(p))
|
||||
self.files.append(FileItem.from_dict(p))
|
||||
else:
|
||||
self.files.append(models.FileItem(path=str(p)))
|
||||
self.files.append(FileItem(path=str(p)))
|
||||
import copy
|
||||
self.context_files = []
|
||||
for f in self.files:
|
||||
if isinstance(f, models.FileItem):
|
||||
if isinstance(f, FileItem):
|
||||
fi = copy.deepcopy(f)
|
||||
else:
|
||||
fi = models.FileItem(path=str(f))
|
||||
fi = FileItem(path=str(f))
|
||||
self.context_files.append(fi)
|
||||
if hasattr(self, "_app") and self._app is not None:
|
||||
self._app.ui_selected_context_files = {f.path for f in self.context_files if f.auto_aggregate}
|
||||
@@ -3287,7 +3293,7 @@ class AppController:
|
||||
if result.ok:
|
||||
self.active_track = result.data
|
||||
raw_tickets = at_data.get("tickets", [])
|
||||
self.active_tickets = [models.Ticket.from_dict(t) if isinstance(t, dict) else t for t in raw_tickets]
|
||||
self.active_tickets = [Ticket.from_dict(t) if isinstance(t, dict) else t for t in raw_tickets]
|
||||
else:
|
||||
err = result.errors[0]
|
||||
self._last_request_errors.append(("active_track_deserialize", err))
|
||||
@@ -3320,9 +3326,9 @@ class AppController:
|
||||
ai_client.set_bias_profile(self.ui_active_bias_profile)
|
||||
raw_presets = proj.get("view_presets", [])
|
||||
if isinstance(raw_presets, dict):
|
||||
self.view_presets = [models.NamedViewPreset.from_dict({"name": name, **data}) for name, data in raw_presets.items()]
|
||||
self.view_presets = [NamedViewPreset.from_dict({"name": name, **data}) for name, data in raw_presets.items()]
|
||||
else:
|
||||
self.view_presets = [models.NamedViewPreset.from_dict(p) for p in raw_presets if isinstance(p, dict)]
|
||||
self.view_presets = [NamedViewPreset.from_dict(p) for p in raw_presets if isinstance(p, dict)]
|
||||
if self.rag_config and self.rag_config.enabled:
|
||||
self._rebuild_rag_index()
|
||||
|
||||
@@ -3396,11 +3402,11 @@ class AppController:
|
||||
summarize._summary_cache.clear()
|
||||
self._push_mma_state_update()
|
||||
|
||||
def save_context_preset(self, preset: models.ContextPreset) -> None:
|
||||
def save_context_preset(self, preset: ContextPreset) -> None:
|
||||
self.context_preset_manager.save_preset(self.project, preset)
|
||||
self._save_active_project()
|
||||
|
||||
def load_context_preset(self, name: str) -> models.ContextPreset:
|
||||
def load_context_preset(self, name: str) -> ContextPreset:
|
||||
presets_result = self.context_preset_manager.load_all(self.project)
|
||||
if not presets_result.ok:
|
||||
raise RuntimeError(f"Failed to load context presets: {presets_result.errors}")
|
||||
@@ -3413,7 +3419,7 @@ class AppController:
|
||||
import copy
|
||||
self.context_files = []
|
||||
for f in preset.files:
|
||||
fi = models.FileItem(path=f.path, view_mode=f.view_mode)
|
||||
fi = FileItem(path=f.path, view_mode=f.view_mode)
|
||||
fi.custom_slices = copy.deepcopy(f.custom_slices)
|
||||
fi.ast_mask = copy.deepcopy(f.ast_mask)
|
||||
fi.ast_signatures = getattr(f, 'ast_signatures', False)
|
||||
@@ -3648,7 +3654,7 @@ class AppController:
|
||||
"""
|
||||
if not name or not name.strip():
|
||||
raise ValueError("Preset name cannot be empty or whitespace.")
|
||||
preset = models.Preset(
|
||||
preset = Preset(
|
||||
name=name,
|
||||
system_prompt=content
|
||||
)
|
||||
@@ -3666,7 +3672,7 @@ class AppController:
|
||||
"""
|
||||
[C: src/gui_2.py:App._render_tool_preset_manager_content]
|
||||
"""
|
||||
preset = models.ToolPreset(name=name, categories=categories)
|
||||
preset = ToolPreset(name=name, categories=categories)
|
||||
self.tool_preset_manager.save_preset(preset, scope)
|
||||
self.tool_presets = self.tool_preset_manager.load_all_presets()
|
||||
|
||||
@@ -3677,7 +3683,7 @@ class AppController:
|
||||
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"):
|
||||
def _cb_save_bias_profile(self, profile: BiasProfile, scope: str = "project"):
|
||||
"""
|
||||
[C: src/gui_2.py:App._render_tool_preset_manager_content]
|
||||
"""
|
||||
@@ -3688,7 +3694,7 @@ class AppController:
|
||||
self.tool_preset_manager.delete_bias_profile(name, scope)
|
||||
self.bias_profiles = self.tool_preset_manager.load_all_bias_profiles()
|
||||
|
||||
def _cb_save_persona(self, persona: models.Persona, scope: str = "project") -> None:
|
||||
def _cb_save_persona(self, persona: Persona, scope: str = "project") -> None:
|
||||
"""
|
||||
[C: src/gui_2.py:App._render_persona_editor_window]
|
||||
"""
|
||||
@@ -3702,11 +3708,11 @@ class AppController:
|
||||
self.persona_manager.delete_persona(name, scope)
|
||||
self.personas = self.persona_manager.load_all()
|
||||
|
||||
def _cb_save_view_preset(self, name: str, f_item: models.FileItem) -> None:
|
||||
def _cb_save_view_preset(self, name: str, f_item: FileItem) -> None:
|
||||
"""
|
||||
[C: src/gui_2.py:App._render_context_files_table, tests/test_view_presets.py:test_save_view_preset]
|
||||
"""
|
||||
preset = models.NamedViewPreset(
|
||||
preset = NamedViewPreset(
|
||||
name=name,
|
||||
view_mode=f_item.view_mode,
|
||||
ast_mask=copy.deepcopy(f_item.ast_mask) if hasattr(f_item, "ast_mask") else {},
|
||||
@@ -3720,7 +3726,7 @@ class AppController:
|
||||
self.view_presets.append(preset)
|
||||
self._flush_to_project()
|
||||
|
||||
def _cb_apply_view_preset(self, name: str, f_item: models.FileItem) -> None:
|
||||
def _cb_apply_view_preset(self, name: str, f_item: FileItem) -> None:
|
||||
"""
|
||||
[C: src/gui_2.py:App._render_context_files_table, tests/test_view_presets.py:test_apply_view_preset]
|
||||
"""
|
||||
@@ -3776,7 +3782,7 @@ class AppController:
|
||||
self.discussion_sent_system_prompt = disc_data.get("sent_system_prompt", "")
|
||||
if "context_snapshot" in disc_data:
|
||||
snapshot_data = disc_data["context_snapshot"]
|
||||
self.context_files = [models.FileItem.from_dict(f) if isinstance(f, dict) else models.FileItem(path=str(f)) for f in snapshot_data]
|
||||
self.context_files = [FileItem.from_dict(f) if isinstance(f, dict) else FileItem(path=str(f)) for f in snapshot_data]
|
||||
if self._app:
|
||||
self._app.ui_selected_context_files = {f.path for f in self.context_files if f.auto_aggregate}
|
||||
self.ai_status = f"discussion: {name}"
|
||||
@@ -3913,8 +3919,8 @@ class AppController:
|
||||
# unsynced forever (test_rag_phase4_final_verify regression on
|
||||
# 2026-06-10).
|
||||
self.rag_engine = None
|
||||
from src import models as _rag_models
|
||||
self.rag_config = _rag_models.RAGConfig()
|
||||
from src.mcp_client import RAGConfig
|
||||
self.rag_config = RAGConfig()
|
||||
self.rag_status = 'idle'
|
||||
self._rag_sync_token = 0
|
||||
self._rag_sync_dirty = False
|
||||
@@ -4720,7 +4726,7 @@ class AppController:
|
||||
"""Phase 6 Group 6.7: topological sort with Result propagation.
|
||||
On ValueError: fall back to raw_tickets (preserves existing behavior)."""
|
||||
try:
|
||||
normalized = [models.Ticket.from_dict(t) if isinstance(t, dict) else t for t in raw_tickets]
|
||||
normalized = [Ticket.from_dict(t) if isinstance(t, dict) else t for t in raw_tickets]
|
||||
sorted_tickets_data = conductor_tech_lead.topological_sort(normalized)
|
||||
return Result(data=sorted_tickets_data)
|
||||
except ValueError as e:
|
||||
@@ -4773,7 +4779,7 @@ class AppController:
|
||||
# 3. Create Track and Ticket objects
|
||||
tickets = []
|
||||
for t_data in sorted_tickets_data:
|
||||
ticket = models.Ticket(
|
||||
ticket = Ticket(
|
||||
id=t_data["id"],
|
||||
description=t_data.get("description") or t_data.get("goal", "No description"),
|
||||
status=t_data.get("status", "todo"),
|
||||
@@ -4783,10 +4789,10 @@ class AppController:
|
||||
)
|
||||
tickets.append(ticket)
|
||||
track_id = f"track_{uuid.uuid5(uuid.NAMESPACE_DNS, f'{self.active_project_path}_{title}').hex[:12]}"
|
||||
track = models.Track(id=track_id, description=title, tickets=tickets)
|
||||
track = Track(id=track_id, description=title, tickets=tickets)
|
||||
# Initialize track state in the filesystem
|
||||
meta = models.Metadata(id=track_id, name=title, status="todo", created_at=datetime.now(), updated_at=datetime.now())
|
||||
state = models.TrackState(metadata=meta, discussion=[], tasks=tickets)
|
||||
state = TrackState(metadata=meta, discussion=[], tasks=tickets)
|
||||
project_manager.save_track_state(track_id, state, self.active_project_root)
|
||||
# Add to memory and notify UI
|
||||
self.tracks.append({"id": track_id, "title": title, "status": "todo"})
|
||||
@@ -5031,10 +5037,10 @@ class AppController:
|
||||
tickets = []
|
||||
for t in state.tasks:
|
||||
if isinstance(t, dict):
|
||||
tickets.append(models.Ticket(**t))
|
||||
tickets.append(Ticket(**t))
|
||||
else:
|
||||
tickets.append(t)
|
||||
self.active_track = models.Track(
|
||||
self.active_track = Track(
|
||||
id=state.metadata.id,
|
||||
description=state.metadata.name,
|
||||
tickets=tickets
|
||||
@@ -5084,7 +5090,7 @@ class AppController:
|
||||
track = self.active_track
|
||||
if track is None: return OK
|
||||
new_tickets = [
|
||||
models.Ticket(
|
||||
Ticket(
|
||||
id=t.id,
|
||||
description=t.description,
|
||||
status=t.status,
|
||||
@@ -5094,7 +5100,7 @@ class AppController:
|
||||
for t in self.active_tickets
|
||||
]
|
||||
track.tickets = new_tickets
|
||||
state = models.TrackState(metadata=track, tasks=list(new_tickets))
|
||||
state = TrackState(metadata=track, tasks=list(new_tickets))
|
||||
project_manager.save_track_state(track.id, state, self.active_project_root)
|
||||
return OK
|
||||
except (OSError, IOError, ValueError, TypeError, KeyError, AttributeError) as e:
|
||||
@@ -5121,7 +5127,7 @@ class AppController:
|
||||
beads_result = self._load_beads_from_path_result(Path(base))
|
||||
if beads_result.ok:
|
||||
for bead in beads_result.data:
|
||||
self.active_tickets.append(models.Ticket(
|
||||
self.active_tickets.append(Ticket(
|
||||
id=bead.id,
|
||||
description=bead.description or "",
|
||||
status=bead.status,
|
||||
|
||||
Reference in New Issue
Block a user