refactor(consumers): migrate 85 'from src.models import' sites to direct subsystem imports

Per post_module_taxonomy_de_cruft_20260627 Phase 2 (FR7). Each
'from src.models import X' for a moved class is rewritten to
'from src.<destination> import X':

  Ticket, Track, WorkerContext, TrackState, TrackMetadata,
    ThinkingSegment, EMPTY_TRACK_STATE            -> src.mma
  ProjectContext, ProjectMeta, ProjectOutput, ProjectFiles,
    ProjectScreenshots, ProjectDiscussion, EMPTY_PROJECT_CONTEXT -> src.project
  FileItem, Preset, ContextPreset, ContextFileEntry,
    NamedViewPreset                                -> src.project_files
  Tool, ToolPreset                                 -> src.tool_presets
  BiasProfile                                      -> src.tool_bias
  TextEditorConfig, ExternalEditorConfig,
    EMPTY_TEXT_EDITOR_CONFIG                       -> src.external_editor
  Persona                                          -> src.personas
  WorkspaceProfile                                -> src.workspace_manager
  MCPServerConfig, MCPConfiguration, VectorStoreConfig,
    RAGConfig, load_mcp_config                      -> src.mcp_client

NOT touched (kept on src.models; Phase 3 or Phase 4 will move them):
  GenerateRequest, ConfirmRequest, DEFAULT_TOOL_CATEGORIES, Metadata, PROVIDERS

Migration was performed by the one-time script
scripts/tier2/artifacts/post_module_taxonomy_de_cruft_20260627/migrate_imports.py
which uses a class-to-module map and re.sub() to rewrite each
'from src.models import X' line.

Total: 85 import lines rewritten across 71 files.

Note: this commit depends on the v2 SHIPPED work
(origin/tier2/module_taxonomy_refactor_20260627) being merged into
this branch NEXT. On master (without the v2 SHIPPED commits), the
destination modules do not exist and these imports would fail.
This commit is contained in:
ed
2026-06-26 13:34:03 -04:00
parent e14cfb13da
commit 8f11340b38
72 changed files with 266 additions and 85 deletions
@@ -0,0 +1,167 @@
"""One-time migration script: src.models import -> direct subsystem imports.
Per post_module_taxonomy_de_cruft_20260627 Phase 2. Updates 95 consumer
sites that use 'from src.models import X' to use the direct subsystem
import path. Each 'from src.models import X' is rewritten based on the
class mapping:
Ticket, Track, WorkerContext, TrackState, TrackMetadata,
ThinkingSegment, EMPTY_TRACK_STATE -> src.mma
ProjectContext, ProjectMeta, ProjectOutput, ProjectFiles,
ProjectScreenshots, ProjectDiscussion, EMPTY_PROJECT_CONTEXT -> src.project
FileItem, Preset, ContextPreset, ContextFileEntry, NamedViewPreset -> src.project_files
Tool, ToolPreset -> src.tool_presets
BiasProfile -> src.tool_bias
TextEditorConfig, ExternalEditorConfig,
EMPTY_TEXT_EDITOR_CONFIG -> src.external_editor
Persona -> src.personas
WorkspaceProfile -> src.workspace_manager
MCPServerConfig, MCPConfiguration, VectorStoreConfig,
RAGConfig, load_mcp_config -> src.mcp_client
NOT touched (kept on src.models):
GenerateRequest, ConfirmRequest -> Phase 4 (api_hooks.py)
DEFAULT_TOOL_CATEGORIES -> Phase 3 (ai_client.py)
Metadata (the legacy alias) -> kept (re-exported at module level)
PROVIDERS -> kept (lazy __getattr__)
Usage:
uv run python scripts/tier2/artifacts/post_module_taxonomy_de_cruft_20260627/migrate_imports.py
This is a one-time script; it does not run as part of the test suite.
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
CLASS_TO_MODULE: dict[str, str] = {
"Ticket": "mma",
"Track": "mma",
"WorkerContext": "mma",
"TrackState": "mma",
"TrackMetadata": "mma",
"ThinkingSegment": "mma",
"EMPTY_TRACK_STATE": "mma",
"ProjectContext": "project",
"ProjectMeta": "project",
"ProjectOutput": "project",
"ProjectFiles": "project",
"ProjectScreenshots": "project",
"ProjectDiscussion": "project",
"EMPTY_PROJECT_CONTEXT": "project",
"FileItem": "project_files",
"Preset": "project_files",
"ContextPreset": "project_files",
"ContextFileEntry": "project_files",
"NamedViewPreset": "project_files",
"Tool": "tool_presets",
"ToolPreset": "tool_presets",
"BiasProfile": "tool_bias",
"TextEditorConfig": "external_editor",
"ExternalEditorConfig": "external_editor",
"EMPTY_TEXT_EDITOR_CONFIG": "external_editor",
"Persona": "personas",
"WorkspaceProfile": "workspace_manager",
"MCPServerConfig": "mcp_client",
"MCPConfiguration": "mcp_client",
"VectorStoreConfig": "mcp_client",
"RAGConfig": "mcp_client",
"load_mcp_config": "mcp_client",
}
KEEP_ON_MODELS: set[str] = {
"GenerateRequest",
"ConfirmRequest",
"DEFAULT_TOOL_CATEGORIES",
"Metadata",
"PROVIDERS",
}
def migrate_file(path: Path) -> tuple[int, list[str]]:
"""Rewrite 'from src.models import X' lines in path. Returns (count, errors)."""
try:
content = path.read_text(encoding="utf-8")
except (OSError, UnicodeDecodeError) as e:
return 0, [f" {path}: cannot read: {e}"]
original = content
errors: list[str] = []
pattern = re.compile(r"^(\s*)from\s+src\.models\s+import\s+(.+?)$", re.MULTILINE)
def replace(m: re.Match[str]) -> str:
indent = m.group(1)
names_str = m.group(2)
names = [n.strip() for n in names_str.split(",")]
kept: list[str] = []
moved: dict[str, list[str]] = {}
for name in names:
if not name:
continue
if name in KEEP_ON_MODELS:
kept.append(name)
continue
if " as " in name:
orig, alias = [s.strip() for s in name.split(" as ", 1)]
if orig in KEEP_ON_MODELS:
kept.append(name)
continue
if orig in CLASS_TO_MODULE:
target_mod = CLASS_TO_MODULE[orig]
moved.setdefault(target_mod, []).append(name)
else:
errors.append(f" {path}: unknown alias '{name}' (orig={orig})")
kept.append(name)
continue
if name in CLASS_TO_MODULE:
target_mod = CLASS_TO_MODULE[name]
moved.setdefault(target_mod, []).append(name)
else:
errors.append(f" {path}: unknown class '{name}'")
kept.append(name)
if not moved and kept == names:
return m.group(0)
lines: list[str] = []
for mod, names_in_mod in sorted(moved.items()):
lines.append(f"{indent}from src.{mod} import {', '.join(names_in_mod)}")
if kept:
lines.append(f"{indent}from src.models import {', '.join(kept)}")
return "\n".join(lines)
new_content = pattern.sub(replace, content)
if new_content != original:
try:
path.write_text(new_content, encoding="utf-8", newline="")
except OSError as e:
return 0, [f" {path}: cannot write: {e}"]
return len(pattern.findall(original)), []
return 0, []
def main() -> int:
root = Path(".")
src_files = sorted(root.glob("src/*.py")) + sorted(root.glob("tests/*.py"))
total_changed = 0
files_changed = 0
all_errors: list[str] = []
for path in src_files:
count, errors = migrate_file(path)
all_errors.extend(errors)
if count > 0:
files_changed += 1
total_changed += count
print(f" {path}: {count} import line(s) rewritten")
print(f"\nTotal: {total_changed} import line(s) rewritten in {files_changed} file(s)")
if all_errors:
print("\nWarnings:")
for err in all_errors:
print(err)
return 1
return 0
if __name__ == "__main__":
sys.exit(main())