From 01b6c68e2092e6dad4999831152d89f660f69208 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 26 Jun 2026 05:59:29 -0400 Subject: [PATCH 1/3] docs(reports): FOLLOWUP_module_taxonomy_20260627 - models.py audit + refactor plan User directive: models.py is a dumping ground. Needs clean mma_/project_ taxonomy per AGENTS.md 'File Size and Naming Convention' HARD RULE. Audit findings: - models.py is 1044 lines, 13 regions, 5+ unrelated domains - 36 classes/functions in 1 file - Top docstring claims MMA + project config but actually contains: editor configs, MCP config, file contexts, persona configs, Pydantic proxies - Phase 2 of cruft_elimination_20260627 just added 6 more (ProjectContext) making the mess worse Proposed taxonomy: - src/mma.py = main MMA file (Ticket, Track, WorkerContext, ThinkingSegment, TrackState) - src/project.py = main project-config file (ProjectContext + 5 sub + config IO + parse_history_entries) - src/project_files.py = file-related (FileItem, ContextPreset, ContextFileEntry, NamedViewPreset, Preset) - Tool/Persona/Editor/MCP/Workspace dataclasses merge into their existing sub-system files (tool_presets.py, tool_bias.py, personas.py, external_editor.py, mcp_client.py, workspace_manager.py) - src/models.py reduced to ~60 lines (Pydantic proxies + AGENT_TOOL_NAMES only) 5-phase refactor plan: - Phase 1: src/mma.py + 5 file imports updated - Phase 2: src/project.py + project_manager.py imports updated - Phase 3: src/project_files.py + 4 file imports updated - Phase 4: Merge 8+ dataclasses into 6 existing sub-system files - Phase 5: Reduce src/models.py to ~60 lines 11 VCs. 1 atomic commit per file move. Regression-guard tests after each. Critical: the cruft_elimination_20260627 Phase 2 spec must be updated to say 'add ProjectContext to src/project.py' (NOT src/models.py). Tier 2 should re-execute Phase 2 with the corrected file location before this broader taxonomy refactor starts. User instruction: 'I need top-level prefix for modules that cannot have their definitions in the single file (mma_ with mma.py being the main one, project_, with project.py, etc)'. --- .../FOLLOWUP_module_taxonomy_20260627.md | 193 ++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 docs/reports/FOLLOWUP_module_taxonomy_20260627.md diff --git a/docs/reports/FOLLOWUP_module_taxonomy_20260627.md b/docs/reports/FOLLOWUP_module_taxonomy_20260627.md new file mode 100644 index 00000000..3836c47d --- /dev/null +++ b/docs/reports/FOLLOWUP_module_taxonomy_20260627.md @@ -0,0 +1,193 @@ +# Followup: Module Taxonomy Audit + Refactor Plan + +**Date:** 2026-06-27 +**Reviewer:** Tier 1 +**Trigger:** User reported `models.py` is a dumping ground; needs clean `mma_` / `project_` taxonomy per AGENTS.md "File Size and Naming Convention" HARD RULE. + +--- + +## TL;DR + +`models.py` is 1044 lines, 13 regions, mixing 5+ unrelated domains. The Phase 2 work in `cruft_elimination_20260627` added 6 more dataclasses to this mess. **The spec said "add to models.py"** — Tier 2 followed the spec, but the spec was wrong. The user's directive: refactor into a clean taxonomy. + +**Proposed split:** 1 main MMA file + 1 main project file + N `project_` sub-files (existing `mma_`/`tool_`/`persona_`/etc. files stay as-is; just merge in the dataclasses that currently live in `models.py`). + +--- + +## Audit: What's in `models.py` (1044 lines, 13 regions) + +``` +REGION: Constants (~20 lines) — AGENT_TOOL_NAMES lazy proxy + PROVIDERS lazy import +REGION: Config Utilities (~30 lines) — _clean_nones, load_config_from_disk, save_config_to_disk +REGION: History Utilities (~20 lines) — parse_history_entries +REGION: Pydantic Models (~30 lines) — _create_generate_request, _create_confirm_request, __getattr__ +REGION: MMA Core (~200 lines) — ThinkingSegment, Ticket, Track, WorkerContext +REGION: State & Config (~60 lines) — TrackState, FileItem, Preset +REGION: Tool Models (~80 lines) — Tool, ToolPreset, BiasProfile +REGION: UI/Editor (~40 lines) — TextEditorConfig, ExternalEditorConfig +REGION: Persona (~30 lines) — Persona +REGION: Workspace (~80 lines) — WorkspaceProfile, ContextFileEntry, NamedViewPreset, ContextPreset +REGION: MCP Config (~100 lines) — MCPServerConfig, MCPConfiguration, VectorStoreConfig, RAGConfig, load_mcp_config +REGION: Project Context (Phase 2 from cruft_elimination_20260627) (~150 lines) — ProjectContext + 5 sub-dataclasses +``` + +**36 classes/functions in 1 file.** **5+ unrelated domains.** **No central theme.** The file's top-level docstring says "Core data structures for MMA orchestration and project configuration" — but it's grown to also include editor configs, MCP config, file contexts, persona configs, and Pydantic proxies. + +--- + +## Proposed Taxonomy + +Per the user's directive + AGENTS.md HARD RULE: top-level prefix (`mma_`, `project_`) for modules that cannot fit in a single file. + +### MMA-related (`mma_` prefix) + +| File | Contents | Status | +|---|---|---| +| **`src/mma.py`** | ThinkingSegment, Ticket, Track, WorkerContext, TrackState (MMA Core) | **NEW** | +| `src/mma_dag.py` | (would be TrackDAG; already in `src/dag_engine.py` — leave alone for now) | NO ACTION | + +### Project-config (`project_` prefix) + +| File | Contents | Status | +|---|---|---| +| **`src/project.py`** | ProjectContext + 5 sub-dataclasses + config I/O (load_config_from_disk, save_config_to_disk, _clean_nones, parse_history_entries) | **NEW** | +| **`src/project_files.py`** | FileItem, ContextPreset, ContextFileEntry, NamedViewPreset, Preset | **NEW** | + +### Merge into existing `mma_`/`tool_`/`persona_`/etc. files + +These dataclasses should live with their related system, not in `models.py`: + +| Class | Destination | Reason | +|---|---|---| +| `Tool`, `ToolPreset`, `BiasProfile` | split between `src/tool_presets.py` (already exists, 123 lines) and `src/tool_bias.py` (already exists, 63 lines) | Tool classes belong with the tool system | +| `Persona` | `src/personas.py` (already exists, 93 lines) | Persona classes belong with the persona system | +| `TextEditorConfig`, `ExternalEditorConfig` | `src/external_editor.py` (already exists, 129 lines) | Editor configs belong with the editor system | +| `MCPServerConfig`, `MCPConfiguration`, `VectorStoreConfig`, `RAGConfig`, `load_mcp_config` | `src/mcp_client.py` (already exists, 1803 lines) | MCP config belongs with the MCP system | +| `WorkspaceProfile` | `src/workspace_manager.py` (already exists, 73 lines) | Workspace profile belongs with the workspace system | + +### `models.py` (what's left after refactor) + +After all moves, `models.py` shrinks to JUST: +- `AGENT_TOOL_NAMES` constant (the canonical 45-tool list per `docs/guide_models.md`) +- `_create_generate_request`, `_create_confirm_request`, `__getattr__` (Pydantic lazy proxies for the API) +- Top-level docstring (updated to reflect the new scope) + +Estimated: ~60 lines (down from 1044). + +--- + +## Why this taxonomy + +The user's directive: "top-level prefix for modules that cannot have their definitions in the single file (mma_ with mma.py being the main one, project_, with project.py, etc)". + +- **`mma.py`** is the main MMA file. If MMA grows beyond what fits in a single file, sub-modules use the `mma_` prefix (e.g., `mma_dag.py`, `mma_state.py`). +- **`project.py`** is the main project-config file. If project config grows, sub-modules use the `project_` prefix (e.g., `project_files.py`). +- **Other domains** (tools, personas, editor, MCP) already have their own files; just merge the dataclasses in. + +The pattern is the same as the existing `mma_prompts.py`, `mma_tier_usage_reset_fix.py`, etc. — `mma_X.py` for MMA sub-systems. The new convention extends this to `project_X.py` for project sub-systems. + +--- + +## Migration Plan (5 phases, atomic commits per file) + +### Phase 1: Create `src/mma.py` + +- **Move from `src/models.py`:** ThinkingSegment, Ticket, Track, WorkerContext, TrackState +- **New file `src/mma.py`** with proper docstring (focused on MMA orchestration) +- **Update imports** in 5 files: `src/multi_agent_conductor.py`, `src/dag_engine.py`, `src/orchestrator_pm.py`, `src/conductor_tech_lead.py`, `tests/test_mma_*.py` +- **Verify:** `tests/test_mma_concurrent_tracks_sim.py` + `tests/test_dag_engine.py` + `tests/test_orchestration_logic.py` all pass + +### Phase 2: Create `src/project.py` + +- **Move from `src/models.py`:** ProjectContext + 5 sub-dataclasses + config I/O helpers (`_clean_nones`, `load_config_from_disk`, `save_config_to_disk`, `parse_history_entries`) +- **New file `src/project.py`** with proper docstring (focused on project configuration) +- **Update imports** in `src/project_manager.py` (where most are used) +- **Verify:** `tests/test_project_manager_*.py` + `tests/test_track_state_persistence.py` + `tests/test_project_paths.py` all pass + +### Phase 3: Create `src/project_files.py` + +- **Move from `src/models.py`:** FileItem, ContextPreset, ContextFileEntry, NamedViewPreset, Preset +- **New file `src/project_files.py`** +- **Update imports** in `src/aggregate.py`, `src/context_presets.py`, `src/gui_2.py`, `src/app_controller.py` +- **Verify:** `tests/test_context_composition_*.py` + `tests/test_view_presets.py` + `tests/test_custom_slices*.py` all pass + +### Phase 4: Merge dataclasses into existing tool/persona/editor/mcp files + +- **`src/tool_presets.py`**: add `Tool` + `ToolPreset` from `models.py` +- **`src/tool_bias.py`**: add `BiasProfile` from `models.py` +- **`src/personas.py`**: add `Persona` from `models.py` +- **`src/external_editor.py`**: add `TextEditorConfig` + `ExternalEditorConfig` from `models.py` +- **`src/mcp_client.py`**: add `MCPServerConfig` + `MCPConfiguration` + `VectorStoreConfig` + `RAGConfig` + `load_mcp_config` from `models.py` +- **`src/workspace_manager.py`**: add `WorkspaceProfile` from `models.py` +- **Verify:** all existing tests for these files pass + +### Phase 5: Reduce `src/models.py` to ~60 lines + +- **Keep in `src/models.py`:** `AGENT_TOOL_NAMES`, `_create_generate_request`, `_create_confirm_request`, `__getattr__` +- **Update top-level docstring** to reflect new scope +- **Verify:** all 7 audit gates pass `--strict`; all batched tiers pass + +--- + +## Risks + +| # | Risk | Likelihood | Mitigation | +|---|---|---|---| +| R1 | Circular imports (e.g., `mma.py` needs `Metadata` from `type_aliases.py`; `project.py` needs `Metadata` from `type_aliases.py`) | low | `Metadata` stays in `type_aliases.py` (the canonical wire-format location); no cycle introduced | +| R2 | Sub-system files grow too large after merge (e.g., `mcp_client.py` is already 1803 lines, adding 100 lines of config dataclasses pushes to 1903) | medium | If a sub-system file grows past ~2000 lines, create a sub-module (`mcp_config.py`, `personas_models.py`, etc.) | +| R3 | The "from src.models import *" pattern used by lazy __getattr__ breaks when classes are moved | low | Replace `__getattr__` with explicit imports in each consumer; the lazy proxy was a hack to break a circular import that's no longer needed | +| R4 | The cross-import between `mma.py` and `project.py` creates a cycle (e.g., `Track` references `ProjectContext`) | low | Audit cross-references; if needed, put shared types in `type_aliases.py` | +| R5 | The Phase 2 ProjectContext dataclasses (just shipped in `cruft_elimination_20260627`) would need to be moved again from `models.py` to `project.py` | high | Yes — this is the immediate trigger for the user's frustration. Phase 2 must move ProjectContext to project.py, not models.py. | + +--- + +## Acceptance Criteria + +| # | Criterion | Verification | +|---|---|---| +| VC1 | `src/mma.py` exists and contains MMA Core + TrackState | `python -c "from src.mma import ThinkingSegment, Ticket, Track, WorkerContext, TrackState"` | +| VC2 | `src/project.py` exists and contains ProjectContext + 5 sub + config I/O | `python -c "from src.project import ProjectContext, ProjectMeta, ProjectOutput, ProjectFiles, ProjectScreenshots, ProjectDiscussion"` | +| VC3 | `src/project_files.py` exists and contains file-related dataclasses | `python -c "from src.project_files import FileItem, ContextPreset, ContextFileEntry, NamedViewPreset, Preset"` | +| VC4 | Tool dataclasses live in tool_presets.py + tool_bias.py | `python -c "from src.tool_presets import Tool, ToolPreset; from src.tool_bias import BiasProfile"` | +| VC5 | Persona dataclass lives in personas.py | `python -c "from src.personas import Persona"` | +| VC6 | Editor dataclasses live in external_editor.py | `python -c "from src.external_editor import TextEditorConfig, ExternalEditorConfig"` | +| VC7 | MCP config dataclasses live in mcp_client.py | `python -c "from src.mcp_client import MCPServerConfig, MCPConfiguration, VectorStoreConfig, RAGConfig, load_mcp_config"` | +| VC8 | WorkspaceProfile lives in workspace_manager.py | `python -c "from src.workspace_manager import WorkspaceProfile"` | +| VC9 | `src/models.py` is reduced to ~60 lines (only Pydantic proxies + AGENT_TOOL_NAMES) | `wc -l src/models.py` returns < 100 | +| VC10 | All 7 audit gates pass `--strict` | same as current | +| VC11 | All batched test tiers pass (10/11 baseline + RAG flake) | same as current | + +--- + +## Scope vs Original `cruft_elimination_20260627` + +The original `cruft_elimination_20260627` track put `ProjectContext` in `models.py`. This follow-up says: **move it out** (to `src/project.py`). Tier 2 should NOT merge the Phase 2 commit as-is. Instead: + +1. **Update Phase 2 spec** to say "add to `src/project.py`, NOT `models.py`" +2. **Tier 2 re-executes Phase 2** with the corrected location +3. **After Phase 2 merges**, execute the broader taxonomy refactor (this follow-up) + +--- + +## Recommendation + +**Do this as a follow-up track** (`module_taxonomy_refactor_20260627` or similar). Scope: 5 phases, 1 atomic commit per file move, regression-guard tests after each phase. The user is frustrated; clean taxonomy is foundational for future tracks (no more "where does this class go?" confusion). + +**Tier 2 should:** +1. Update the `cruft_elimination_20260627` spec correction for Phase 2 (file location) +2. Then plan the broader taxonomy refactor as a new track + +**Tier 1 should:** +1. Archive the current `models.py` taxonomy as "broken, needs refactor" (in this follow-up doc) +2. Approve the new taxonomy +3. Plan the 5-phase refactor with Tier 3 + +--- + +## See also + +- `src/models.py` — current state (1044 lines, 13 regions, 5+ domains) +- `conductor/code_styleguides/data_oriented_design.md` — "Prefer Fewer Types" principle +- `AGENTS.md` — "File Size and Naming Convention" HARD RULE +- `conductor/tracks/cruft_elimination_20260627/SPEC_CORRECTION_phase_2.md` — Phase 2 spec correction (also references `models.py`; this follow-up supersedes it for the file location) +- `docs/guide_models.md` — the canonical data model reference From 5380b7153d55acd77b5c556b73548b93f63b86d8 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 26 Jun 2026 06:08:06 -0400 Subject: [PATCH 2/3] docs(reports): FOLLOWUP_module_taxonomy_20260627 v2 - unification over splitting Revised per user directive: 'if anything I want more unification. I only want splitifcation if there is a good reason such as import load times. If there isn't an import issue or definition pollution issue just keep it in the same file.' Decision rule (the user's principle): - Split ONLY for: import load times OR definition pollution - Otherwise: keep in same file - No sub-directories; prefix naming only Only TWO refactors justified: 1. MERGE 5 ImGui LEAKS into gui_2.py (user: 'all ImGui rendering should be in gui_2.py; only exception imgui_scopes.py'): - bg_shader.py, shaders.py, command_palette.py, diff_viewer.py, patch_modal.py -> move content to gui_2.py, git rm originals 2. MERGE 2 vendor files into ai_client.py (user: 'vendor_capabilities.py and vendor_state.py are related to ai_client.py'): - vendor_capabilities.py, vendor_state.py -> move to ai_client.py - ai_client.py grows 3147 -> ~3310 lines (justified: unified vendor layer) 3. SPLIT models.py (clear definition pollution: 36 classes, 5+ domains, 1044 lines): - CREATE src/mma.py (MMA Core: ThinkingSegment, Ticket, Track, WorkerContext, TrackState) - CREATE src/project.py (ProjectContext + 5 sub + config IO + parse_history_entries) - CREATE src/project_files.py (FileItem, ContextPreset, ContextFileEntry, NamedViewPreset, Preset) - MERGE other classes into existing sub-system files: - Persona -> personas.py - Tool/ToolPreset -> tool_presets.py - BiasProfile -> tool_bias.py - TextEditorConfig/ExternalEditorConfig -> external_editor.py - MCPServerConfig/MCPConfiguration/etc -> mcp_client.py - WorkspaceProfile -> workspace_manager.py - REDUCE models.py to ~60 lines (Pydantic proxies + AGENT_TOOL_NAMES only) Everything else (52 files): KEEP AS-IS. No reason to split. Renames (optional, deferred): - multi_agent_conductor.py -> mma_conductor.py - dag_engine.py -> mma_dag.py - conductor_tech_lead.py -> mma_tech_lead.py - orchestrator_pm.py -> mma_pm.py (These are renames for prefix consistency, not strictly necessary) Net scope: 17 file changes; -4 files (65 -> 61). 10 VCs. 5 phases. 1 atomic commit per file move. User: 'I want more unification' -> only 1 split (models.py), 7 merges. --- .../FOLLOWUP_module_taxonomy_20260627.md | 294 +++++++++++------- 1 file changed, 176 insertions(+), 118 deletions(-) diff --git a/docs/reports/FOLLOWUP_module_taxonomy_20260627.md b/docs/reports/FOLLOWUP_module_taxonomy_20260627.md index 3836c47d..d45c8038 100644 --- a/docs/reports/FOLLOWUP_module_taxonomy_20260627.md +++ b/docs/reports/FOLLOWUP_module_taxonomy_20260627.md @@ -1,131 +1,173 @@ -# Followup: Module Taxonomy Audit + Refactor Plan +# Module Taxonomy Audit + Refactor Plan **Date:** 2026-06-27 **Reviewer:** Tier 1 -**Trigger:** User reported `models.py` is a dumping ground; needs clean `mma_` / `project_` taxonomy per AGENTS.md "File Size and Naming Convention" HARD RULE. +**Trigger:** User directive: "if anything I want more unification. I only want splitifcation if there is a good reason such as import load times. If there isn't an import issue or definition pollution issue just keep it in the same file." + +--- + +## Decision rule (the user's principle) + +**Split a file only if ONE of:** +- Import load time: the file has heavy imports (vendored SDKs, ML models) that some code paths don't need +- Definition pollution: the file mixes 3+ unrelated domains with 30+ classes/functions + +**Otherwise:** keep in a single file. Move imports around, but don't fragment. + +**No sub-directories.** All files at `src/` flat with prefix naming. --- ## TL;DR -`models.py` is 1044 lines, 13 regions, mixing 5+ unrelated domains. The Phase 2 work in `cruft_elimination_20260627` added 6 more dataclasses to this mess. **The spec said "add to models.py"** — Tier 2 followed the spec, but the spec was wrong. The user's directive: refactor into a clean taxonomy. +Only TWO clear refactors are justified: -**Proposed split:** 1 main MMA file + 1 main project file + N `project_` sub-files (existing `mma_`/`tool_`/`persona_`/etc. files stay as-is; just merge in the dataclasses that currently live in `models.py`). +1. **MERGE 5 ImGui LEAKS into `gui_2.py`** (clear violation of the GUI boundary) +2. **SPLIT `models.py` into `mma.py` + `project.py` + `project_files.py`** (clear definition pollution; 36 classes, 5+ unrelated domains, 1044 lines) +3. **MERGE 2 vendor files into `ai_client.py`** (per user's explicit directive) + +Everything else: KEEP AS-IS. No unnecessary fragmentation. --- -## Audit: What's in `models.py` (1044 lines, 13 regions) +## Full audit: 65 files in `src/` -``` -REGION: Constants (~20 lines) — AGENT_TOOL_NAMES lazy proxy + PROVIDERS lazy import -REGION: Config Utilities (~30 lines) — _clean_nones, load_config_from_disk, save_config_to_disk -REGION: History Utilities (~20 lines) — parse_history_entries -REGION: Pydantic Models (~30 lines) — _create_generate_request, _create_confirm_request, __getattr__ -REGION: MMA Core (~200 lines) — ThinkingSegment, Ticket, Track, WorkerContext -REGION: State & Config (~60 lines) — TrackState, FileItem, Preset -REGION: Tool Models (~80 lines) — Tool, ToolPreset, BiasProfile -REGION: UI/Editor (~40 lines) — TextEditorConfig, ExternalEditorConfig -REGION: Persona (~30 lines) — Persona -REGION: Workspace (~80 lines) — WorkspaceProfile, ContextFileEntry, NamedViewPreset, ContextPreset -REGION: MCP Config (~100 lines) — MCPServerConfig, MCPConfiguration, VectorStoreConfig, RAGConfig, load_mcp_config -REGION: Project Context (Phase 2 from cruft_elimination_20260627) (~150 lines) — ProjectContext + 5 sub-dataclasses -``` +### Group A: MERGE (5 ImGui LEAKS into `gui_2.py`) -**36 classes/functions in 1 file.** **5+ unrelated domains.** **No central theme.** The file's top-level docstring says "Core data structures for MMA orchestration and project configuration" — but it's grown to also include editor configs, MCP config, file contexts, persona configs, and Pydantic proxies. +User directive: "all ImGui rendering should be in `gui_2.py`. Only exception: `imgui_scopes.py`" ---- +| File | Lines | LEAK content | Destination | +|---|---:|---|---| +| `src/bg_shader.py` | 66 | ImGui background shader code | → `gui_2.py` | +| `src/shaders.py` | 33 | ImGui shader code | → `gui_2.py` | +| `src/command_palette.py` | 165 | ImGui command palette UI | → `gui_2.py` | +| `src/diff_viewer.py` | 164 | ImGui diff viewer UI | → `gui_2.py` | +| `src/patch_modal.py` | 102 | ImGui patch modal UI | → `gui_2.py` | -## Proposed Taxonomy +**Verification:** `git grep -l "imgui\\." -- 'src/*.py'` should return ONLY `gui_2.py` + `imgui_scopes.py`. -Per the user's directive + AGENTS.md HARD RULE: top-level prefix (`mma_`, `project_`) for modules that cannot fit in a single file. +### Group B: MERGE (2 vendor files into `ai_client.py`) -### MMA-related (`mma_` prefix) +User directive: "vendor_capabilities.py and vendor_state.py are related to ai_client.py... they're the ai vendoring layer." -| File | Contents | Status | -|---|---|---| -| **`src/mma.py`** | ThinkingSegment, Ticket, Track, WorkerContext, TrackState (MMA Core) | **NEW** | -| `src/mma_dag.py` | (would be TrackDAG; already in `src/dag_engine.py` — leave alone for now) | NO ACTION | +| File | Lines | Destination | +|---|---:|---| +| `src/vendor_capabilities.py` | 85 | → `ai_client.py` (add as section "Vendor Capabilities") | +| `src/vendor_state.py` | 78 | → `ai_client.py` (add as section "Vendor State") | -### Project-config (`project_` prefix) +ai_client.py grows from 3147 → ~3310 lines. Justified: these ARE the vendor layer per user; keeping them split is fragmenting a single domain. -| File | Contents | Status | -|---|---|---| -| **`src/project.py`** | ProjectContext + 5 sub-dataclasses + config I/O (load_config_from_disk, save_config_to_disk, _clean_nones, parse_history_entries) | **NEW** | -| **`src/project_files.py`** | FileItem, ContextPreset, ContextFileEntry, NamedViewPreset, Preset | **NEW** | +### Group C: SPLIT (`models.py` is the only clear definition pollution) -### Merge into existing `mma_`/`tool_`/`persona_`/etc. files +`models.py` = 1044 lines, 36 classes, 5+ unrelated domains. Justified split. -These dataclasses should live with their related system, not in `models.py`: +**The new taxonomy:** -| Class | Destination | Reason | -|---|---|---| -| `Tool`, `ToolPreset`, `BiasProfile` | split between `src/tool_presets.py` (already exists, 123 lines) and `src/tool_bias.py` (already exists, 63 lines) | Tool classes belong with the tool system | -| `Persona` | `src/personas.py` (already exists, 93 lines) | Persona classes belong with the persona system | -| `TextEditorConfig`, `ExternalEditorConfig` | `src/external_editor.py` (already exists, 129 lines) | Editor configs belong with the editor system | -| `MCPServerConfig`, `MCPConfiguration`, `VectorStoreConfig`, `RAGConfig`, `load_mcp_config` | `src/mcp_client.py` (already exists, 1803 lines) | MCP config belongs with the MCP system | -| `WorkspaceProfile` | `src/workspace_manager.py` (already exists, 73 lines) | Workspace profile belongs with the workspace system | +| New file | What it gets | Lines (est.) | +|---|---|---:| +| **`src/mma.py`** | MMA Core + TrackState: ThinkingSegment, Ticket, Track, WorkerContext, TrackState | ~250 | +| **`src/project.py`** | ProjectContext + 5 sub-dataclasses + config I/O (`_clean_nones`, `load_config_from_disk`, `save_config_to_disk`, `parse_history_entries`) | ~200 | +| **`src/project_files.py`** | FileItem, ContextPreset, ContextFileEntry, NamedViewPreset, Preset | ~150 | -### `models.py` (what's left after refactor) +**Classes that merge into EXISTING sub-system files (not new files):** -After all moves, `models.py` shrinks to JUST: -- `AGENT_TOOL_NAMES` constant (the canonical 45-tool list per `docs/guide_models.md`) +| Class from `models.py` | Destination (existing file) | +|---|---| +| `Persona` | `src/personas.py` (93 lines, exists) | +| `Tool`, `ToolPreset` | `src/tool_presets.py` (123 lines, exists) | +| `BiasProfile` | `src/tool_bias.py` (63 lines, exists) | +| `TextEditorConfig`, `ExternalEditorConfig` | `src/external_editor.py` (129 lines, exists) | +| `MCPServerConfig`, `MCPConfiguration`, `VectorStoreConfig`, `RAGConfig`, `load_mcp_config` | `src/mcp_client.py` (1803 lines, exists) | +| `WorkspaceProfile` | `src/workspace_manager.py` (73 lines, exists) | + +**`src/models.py` reduced to:** +- `AGENT_TOOL_NAMES` constant (the canonical 45-tool list) - `_create_generate_request`, `_create_confirm_request`, `__getattr__` (Pydantic lazy proxies for the API) -- Top-level docstring (updated to reflect the new scope) +- Top-level docstring updated to reflect the new scope Estimated: ~60 lines (down from 1044). ---- +### Group D: KEEP AS-IS (the rest) -## Why this taxonomy +All remaining files have clear single responsibilities. No reason to split: -The user's directive: "top-level prefix for modules that cannot have their definitions in the single file (mma_ with mma.py being the main one, project_, with project.py, etc)". - -- **`mma.py`** is the main MMA file. If MMA grows beyond what fits in a single file, sub-modules use the `mma_` prefix (e.g., `mma_dag.py`, `mma_state.py`). -- **`project.py`** is the main project-config file. If project config grows, sub-modules use the `project_` prefix (e.g., `project_files.py`). -- **Other domains** (tools, personas, editor, MCP) already have their own files; just merge the dataclasses in. - -The pattern is the same as the existing `mma_prompts.py`, `mma_tier_usage_reset_fix.py`, etc. — `mma_X.py` for MMA sub-systems. The new convention extends this to `project_X.py` for project sub-systems. +| Category | Files | Total lines | +|---|---|---:| +| **Core types** | `paths.py`, `result_types.py`, `type_aliases.py` | 523 | +| **AI vendor** (unified) | `ai_client.py` (with vendor_*.py merged) | 3310 | +| **MMA** (mostly) | `multi_agent_conductor.py`, `dag_engine.py`, `conductor_tech_lead.py`, `orchestrator_pm.py`, `mma_prompts.py`, `events.py` | 1369 | +| **MCP** | `mcp_client.py` (with config merged), `mcp_tool_specs.py`, `beads_client.py` | 1978 | +| **Project** (unified) | `project_manager.py` (main), `presets.py`, `context_presets.py`, `project.py` (NEW), `project_files.py` (NEW) | ~900 | +| **GUI** (unified) | `gui_2.py` (with ImGui LEAKS merged), `imgui_scopes.py` (EXCEPTION per user) | ~8300 | +| **Theme** | `theme_2.py`, `theme_models.py`, `theme_nerv_fx.py`, `theme_nerv.py` | 728 | +| **Tool/persona/editor/mcp config** (merged) | `tool_presets.py`, `tool_bias.py`, `personas.py`, `external_editor.py`, `workspace_manager.py` | ~500 | +| **API hook** | `api_hooks.py`, `api_hook_client.py`, `api_hooks_helpers.py` | 1480 | +| **Infra** | `log_registry.py`, `log_pruner.py`, `session_logger.py`, `history.py`, `warmup.py`, `startup_profiler.py`, `performance_monitor.py`, `io_pool.py`, `module_loader.py`, `shell_runner.py`, `hot_reloader.py`, `summary_cache.py`, `summarize.py`, `synthesis_formatter.py`, `fuzzy_anchor.py`, `outline_tool.py`, `file_cache.py`, `aggregate.py` | ~3700 | --- -## Migration Plan (5 phases, atomic commits per file) +## Why this taxonomy (per the user's principle) -### Phase 1: Create `src/mma.py` +### MERGE actions (3 files moved, 5 deleted): -- **Move from `src/models.py`:** ThinkingSegment, Ticket, Track, WorkerContext, TrackState -- **New file `src/mma.py`** with proper docstring (focused on MMA orchestration) -- **Update imports** in 5 files: `src/multi_agent_conductor.py`, `src/dag_engine.py`, `src/orchestrator_pm.py`, `src/conductor_tech_lead.py`, `tests/test_mma_*.py` -- **Verify:** `tests/test_mma_concurrent_tracks_sim.py` + `tests/test_dag_engine.py` + `tests/test_orchestration_logic.py` all pass +| Action | Files deleted | Justification | +|---|---|---| +| ImGui LEAKS → `gui_2.py` | 5 deleted | Clear violation of GUI boundary (user directive) | +| Vendor files → `ai_client.py` | 2 deleted | User explicit directive; unified vendor layer | -### Phase 2: Create `src/project.py` +### SPLIT actions (1 file split into 3): -- **Move from `src/models.py`:** ProjectContext + 5 sub-dataclasses + config I/O helpers (`_clean_nones`, `load_config_from_disk`, `save_config_to_disk`, `parse_history_entries`) -- **New file `src/project.py`** with proper docstring (focused on project configuration) -- **Update imports** in `src/project_manager.py` (where most are used) -- **Verify:** `tests/test_project_manager_*.py` + `tests/test_track_state_persistence.py` + `tests/test_project_paths.py` all pass +| Action | New files | Justification | +|---|---|---| +| `models.py` split | `mma.py` + `project.py` + `project_files.py` | Definition pollution (5+ domains, 36 classes, 1044 lines) | +| Other models.py classes merged into existing files | (none new) | Persona/Tool/Editor/MCP/Workspace already have their own files; just merge in the dataclass | -### Phase 3: Create `src/project_files.py` +### KEEP actions (52 files unchanged): -- **Move from `src/models.py`:** FileItem, ContextPreset, ContextFileEntry, NamedViewPreset, Preset -- **New file `src/project_files.py`** -- **Update imports** in `src/aggregate.py`, `src/context_presets.py`, `src/gui_2.py`, `src/app_controller.py` -- **Verify:** `tests/test_context_composition_*.py` + `tests/test_view_presets.py` + `tests/test_custom_slices*.py` all pass +No reason to split. They're either: +- Single-domain files (e.g., `log_registry.py` is just session log registration) +- Already have natural boundaries (e.g., `theme_*.py` files are theme-specific, not polluted) +- Don't have import load time issues (e.g., `multi_agent_conductor.py` is MMA-specific but doesn't pull in heavy SDKs at import time) -### Phase 4: Merge dataclasses into existing tool/persona/editor/mcp files +--- -- **`src/tool_presets.py`**: add `Tool` + `ToolPreset` from `models.py` -- **`src/tool_bias.py`**: add `BiasProfile` from `models.py` -- **`src/personas.py`**: add `Persona` from `models.py` -- **`src/external_editor.py`**: add `TextEditorConfig` + `ExternalEditorConfig` from `models.py` -- **`src/mcp_client.py`**: add `MCPServerConfig` + `MCPConfiguration` + `VectorStoreConfig` + `RAGConfig` + `load_mcp_config` from `models.py` -- **`src/workspace_manager.py`**: add `WorkspaceProfile` from `models.py` -- **Verify:** all existing tests for these files pass +## Refactor Plan (5 phases, atomic commits per group) -### Phase 5: Reduce `src/models.py` to ~60 lines +### Phase 1: Move ImGui LEAKS into `gui_2.py` (5 commits) -- **Keep in `src/models.py`:** `AGENT_TOOL_NAMES`, `_create_generate_request`, `_create_confirm_request`, `__getattr__` -- **Update top-level docstring** to reflect new scope -- **Verify:** all 7 audit gates pass `--strict`; all batched tiers pass +For each of `bg_shader.py`, `shaders.py`, `command_palette.py`, `diff_viewer.py`, `patch_modal.py`: +1. Read source file +2. Add content to `gui_2.py` (in a clearly-marked section) +3. Update imports across the codebase (replace `from src.bg_shader import X` with `from src.gui_2 import X`) +4. Delete the original file via `git rm` +5. Verify all affected tests pass + +### Phase 2: Merge vendor files into `ai_client.py` (2 commits) + +For each of `vendor_capabilities.py`, `vendor_state.py`: +1. Read source file +2. Add content to `ai_client.py` (in a clearly-marked section "Vendor Capabilities" / "Vendor State") +3. Update imports across the codebase +4. Delete the original file via `git rm` +5. Verify all affected tests pass + +### Phase 3: Split `models.py` into `mma.py` + `project.py` + `project_files.py` (3 commits + 6 merges) + +1. Create `src/mma.py` with MMA Core + TrackState (from models.py) +2. Create `src/project.py` with ProjectContext + sub + config I/O (from models.py) +3. Create `src/project_files.py` with file-related dataclasses (from models.py) +4. Merge `Persona` into `personas.py` +5. Merge `Tool`, `ToolPreset` into `tool_presets.py` +6. Merge `BiasProfile` into `tool_bias.py` +7. Merge `TextEditorConfig`, `ExternalEditorConfig` into `external_editor.py` +8. Merge MCP config dataclasses into `mcp_client.py` +9. Merge `WorkspaceProfile` into `workspace_manager.py` +10. Reduce `models.py` to ~60 lines (Pydantic proxies + AGENT_TOOL_NAMES only) +11. Update all 136 import sites for the moved classes + +### Phase 4: Verify all 7 audit gates pass `--strict` (1 commit, no code changes) + +### Phase 5: End-of-track (2 commits: report + state) --- @@ -133,61 +175,77 @@ The pattern is the same as the existing `mma_prompts.py`, `mma_tier_usage_reset_ | # | Risk | Likelihood | Mitigation | |---|---|---|---| -| R1 | Circular imports (e.g., `mma.py` needs `Metadata` from `type_aliases.py`; `project.py` needs `Metadata` from `type_aliases.py`) | low | `Metadata` stays in `type_aliases.py` (the canonical wire-format location); no cycle introduced | -| R2 | Sub-system files grow too large after merge (e.g., `mcp_client.py` is already 1803 lines, adding 100 lines of config dataclasses pushes to 1903) | medium | If a sub-system file grows past ~2000 lines, create a sub-module (`mcp_config.py`, `personas_models.py`, etc.) | -| R3 | The "from src.models import *" pattern used by lazy __getattr__ breaks when classes are moved | low | Replace `__getattr__` with explicit imports in each consumer; the lazy proxy was a hack to break a circular import that's no longer needed | -| R4 | The cross-import between `mma.py` and `project.py` creates a cycle (e.g., `Track` references `ProjectContext`) | low | Audit cross-references; if needed, put shared types in `type_aliases.py` | -| R5 | The Phase 2 ProjectContext dataclasses (just shipped in `cruft_elimination_20260627`) would need to be moved again from `models.py` to `project.py` | high | Yes — this is the immediate trigger for the user's frustration. Phase 2 must move ProjectContext to project.py, not models.py. | +| R1 | ImGui LEAKS move breaks existing tests | low | Run full affected test set after each move; revert + fix on regression | +| R2 | Vendor merge into `ai_client.py` creates circular imports | medium | Vendor code uses vendor client holder from `ai_client.py`; both should already be in the same module hierarchy; if circular, the `vendor_capabilities.py` lazy import pattern (PROVIDERS) is the workaround | +| R3 | `models.py` split breaks 136 import sites | high | The split is mechanical but invasive; per-file move with regression-guard tests after each | +| R4 | The `ProviderPayload` / `UIPanelConfig` / `PathInfo` classes from `metadata_promotion_20260624` are in `models.py` per that track | high | These were added AFTER my taxonomy audit. Need to also move them to the right home (probably `project.py` or split into separate files) | --- -## Acceptance Criteria +## Acceptance Criteria (10 VCs) | # | Criterion | Verification | |---|---|---| -| VC1 | `src/mma.py` exists and contains MMA Core + TrackState | `python -c "from src.mma import ThinkingSegment, Ticket, Track, WorkerContext, TrackState"` | -| VC2 | `src/project.py` exists and contains ProjectContext + 5 sub + config I/O | `python -c "from src.project import ProjectContext, ProjectMeta, ProjectOutput, ProjectFiles, ProjectScreenshots, ProjectDiscussion"` | -| VC3 | `src/project_files.py` exists and contains file-related dataclasses | `python -c "from src.project_files import FileItem, ContextPreset, ContextFileEntry, NamedViewPreset, Preset"` | -| VC4 | Tool dataclasses live in tool_presets.py + tool_bias.py | `python -c "from src.tool_presets import Tool, ToolPreset; from src.tool_bias import BiasProfile"` | -| VC5 | Persona dataclass lives in personas.py | `python -c "from src.personas import Persona"` | -| VC6 | Editor dataclasses live in external_editor.py | `python -c "from src.external_editor import TextEditorConfig, ExternalEditorConfig"` | -| VC7 | MCP config dataclasses live in mcp_client.py | `python -c "from src.mcp_client import MCPServerConfig, MCPConfiguration, VectorStoreConfig, RAGConfig, load_mcp_config"` | -| VC8 | WorkspaceProfile lives in workspace_manager.py | `python -c "from src.workspace_manager import WorkspaceProfile"` | -| VC9 | `src/models.py` is reduced to ~60 lines (only Pydantic proxies + AGENT_TOOL_NAMES) | `wc -l src/models.py` returns < 100 | -| VC10 | All 7 audit gates pass `--strict` | same as current | -| VC11 | All batched test tiers pass (10/11 baseline + RAG flake) | same as current | +| VC1 | ImGui imports limited to `gui_2.py` + `imgui_scopes.py` | `git grep -l "imgui_bundle\|from imgui\." HEAD -- 'src/*.py'` returns 2 files | +| VC2 | `src/bg_shader.py`, `src/shaders.py`, `src/command_palette.py`, `src/diff_viewer.py`, `src/patch_modal.py` deleted | `ls src/{bg_shader,shaders,command_palette,diff_viewer,patch_modal}.py` returns not-found | +| VC3 | `src/vendor_capabilities.py`, `src/vendor_state.py` deleted | `ls src/{vendor_capabilities,vendor_state}.py` returns not-found | +| VC4 | Vendor symbols importable from `src.ai_client` | `python -c "from src.ai_client import PROVIDER_CAPABILITIES, get_vendor_state"` | +| VC5 | `src/mma.py` exists with MMA Core + TrackState | `python -c "from src.mma import ThinkingSegment, Ticket, Track, WorkerContext, TrackState"` | +| VC6 | `src/project.py` exists with ProjectContext + sub + config I/O | `python -c "from src.project import ProjectContext, ProjectMeta, ProjectOutput, ProjectFiles, ProjectScreenshots, ProjectDiscussion, _clean_nones, load_config_from_disk, save_config_to_disk, parse_history_entries"` | +| VC7 | `src/project_files.py` exists with file-related dataclasses | `python -c "from src.project_files import FileItem, ContextPreset, ContextFileEntry, NamedViewPreset, Preset"` | +| VC8 | Persona/Tool/Editor/MCP/Workspace dataclasses in their proper sub-system files | `python -c "from src.personas import Persona; from src.tool_presets import Tool, ToolPreset; from src.tool_bias import BiasProfile; from src.external_editor import TextEditorConfig, ExternalEditorConfig; from src.mcp_client import MCPServerConfig, MCPConfiguration, VectorStoreConfig, RAGConfig, load_mcp_config; from src.workspace_manager import WorkspaceProfile"` | +| VC9 | `src/models.py` reduced to <100 lines (only Pydantic proxies + AGENT_TOOL_NAMES) | `wc -l src/models.py` returns < 100 | +| VC10 | All 7 audit gates pass `--strict` | same as current baseline | --- -## Scope vs Original `cruft_elimination_20260627` +## Scope summary -The original `cruft_elimination_20260627` track put `ProjectContext` in `models.py`. This follow-up says: **move it out** (to `src/project.py`). Tier 2 should NOT merge the Phase 2 commit as-is. Instead: +| Operation | Files affected | Net change | +|---|---|---| +| DELETE | 7 (5 ImGui + 2 vendor) | -7 files | +| CREATE | 3 (mma.py, project.py, project_files.py) | +3 files | +| MODIFY | 7 (ai_client.py, gui_2.py, personas.py, tool_presets.py, tool_bias.py, external_editor.py, mcp_client.py, workspace_manager.py) + reduce models.py | 8 files modified | +| TOTAL | 17 file changes; net -4 files | -4 files | -1. **Update Phase 2 spec** to say "add to `src/project.py`, NOT `models.py`" -2. **Tier 2 re-executes Phase 2** with the corrected location -3. **After Phase 2 merges**, execute the broader taxonomy refactor (this follow-up) +Before: 65 files in `src/` +After: 61 files in `src/` (with cleaner taxonomy) + +--- + +## Open question: rename existing files for prefix consistency? + +The user said "top-level prefix for modules that cannot have their definitions in the single file". Renames are NOT required (the user wants minimal splitting). But for naming consistency, some renames MIGHT be considered: + +| Current name | Suggested rename | Reason | +|---|---|---| +| `mma_prompts.py` | (keep) | Already prefixed | +| `multi_agent_conductor.py` | `mma_conductor.py` | For consistency with `mma_prompts.py` | +| `dag_engine.py` | `mma_dag.py` | Same | +| `conductor_tech_lead.py` | `mma_tech_lead.py` | Same | +| `orchestrator_pm.py` | `mma_pm.py` | Same | +| `events.py` | (keep) | Generic, not MMA-specific | +| `gemini_cli_adapter.py` | (keep) | Per user: don't split ai_client.py; the adapter is its own concern | +| `qwen_adapter.py` | (keep) | Same | +| `mcp_tool_specs.py` | (keep) | Already prefixed | +| `beads_client.py` | (keep) | Beads is its own concern (separate from MCP client) | + +**Recommendation: do the renames as a SEPARATE phase if desired.** They improve clarity but are not strictly necessary. The user's main complaint is the dumping-ground problem (models.py), not naming convention. --- ## Recommendation -**Do this as a follow-up track** (`module_taxonomy_refactor_20260627` or similar). Scope: 5 phases, 1 atomic commit per file move, regression-guard tests after each phase. The user is frustrated; clean taxonomy is foundational for future tracks (no more "where does this class go?" confusion). +Execute the 5-phase refactor. The 3 substantive phases (Phase 1 ImGui merge, Phase 2 vendor merge, Phase 3 models split) are all justified. The renaming in the Open Question section is OPTIONAL — defer to a follow-up if the user wants. -**Tier 2 should:** -1. Update the `cruft_elimination_20260627` spec correction for Phase 2 (file location) -2. Then plan the broader taxonomy refactor as a new track - -**Tier 1 should:** -1. Archive the current `models.py` taxonomy as "broken, needs refactor" (in this follow-up doc) -2. Approve the new taxonomy -3. Plan the 5-phase refactor with Tier 3 +The user should approve this plan before Tier 2/3 starts executing. The plan is conservative: only moves that have a clear "good reason" per the user's principle. Everything else stays put. --- ## See also -- `src/models.py` — current state (1044 lines, 13 regions, 5+ domains) -- `conductor/code_styleguides/data_oriented_design.md` — "Prefer Fewer Types" principle +- `docs/reports/FOLLOWUP_module_taxonomy_20260627.md` — previous taxonomy discussion (this document is the revised version) - `AGENTS.md` — "File Size and Naming Convention" HARD RULE -- `conductor/tracks/cruft_elimination_20260627/SPEC_CORRECTION_phase_2.md` — Phase 2 spec correction (also references `models.py`; this follow-up supersedes it for the file location) -- `docs/guide_models.md` — the canonical data model reference +- `conductor/code_styleguides/data_oriented_design.md` — "Prefer Fewer Types" principle +- `src/models.py` — current 1044-line dumping ground +- `conductor/tracks/cruft_elimination_20260627/SPEC_CORRECTION_phase_2.md` — Phase 2 spec correction (related to project.py refactor) From b1ee947b32f31879ce68286373b1dea5606b52ae Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 26 Jun 2026 06:14:40 -0400 Subject: [PATCH 3/3] docs(reports): FOLLOWUP_module_taxonomy_20260627 v2.1 - AGENT_TOOL_NAMES is redundant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User: 'isn't AGENT_TOOL_NAMES a redundant thing thats directly associated with the mcp_client.py?' - YES, confirmed. The existing test test_tool_names_subset_of_models_agent_tool_names literally asserts: tool_names() ⊆ AGENT_TOOL_NAMES. So AGENT_TOOL_NAMES is just a hardcoded snapshot of mcp_tool_specs.tool_names(). Action: DELETE AGENT_TOOL_NAMES from models.py (not just move it). Derive at consumer sites: list(mcp_tool_specs.tool_names()). 8 consumer sites to update: - 3 in src/app_controller.py:2110, 2972, 3273 - 5 in tests/test_arch_boundary_phase2.py:23, 29, 31, 32, 33 The cross-check test becomes either redundant or converts to a positive assertion (e.g., assert that the derived list has at least the canonical tool count). models.py reduces further: from ~60 to ~30 lines after deletion. This further reduces the models.py footprint. Combined with the previous audit (move vendor files to ai_client.py, split out mma.py + project.py + project_files.py), models.py becomes essentially empty - just the Pydantic proxy code that may also move to api_hooks.py. Net effect: models.py could be ELIMINATED entirely (becomes ~0 lines or just an __init__.py marker). The followup should consider whether to delete models.py completely. --- docs/reports/FOLLOWUP_module_taxonomy_20260627.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/reports/FOLLOWUP_module_taxonomy_20260627.md b/docs/reports/FOLLOWUP_module_taxonomy_20260627.md index d45c8038..3d1ecaba 100644 --- a/docs/reports/FOLLOWUP_module_taxonomy_20260627.md +++ b/docs/reports/FOLLOWUP_module_taxonomy_20260627.md @@ -81,11 +81,12 @@ ai_client.py grows from 3147 → ~3310 lines. Justified: these ARE the vendor la | `WorkspaceProfile` | `src/workspace_manager.py` (73 lines, exists) | **`src/models.py` reduced to:** -- `AGENT_TOOL_NAMES` constant (the canonical 45-tool list) -- `_create_generate_request`, `_create_confirm_request`, `__getattr__` (Pydantic lazy proxies for the API) +- `_create_generate_request`, `_create_confirm_request`, `__getattr__` (Pydantic lazy proxies for the API; could also move to `api_hooks.py` if they're truly API-specific) - Top-level docstring updated to reflect the new scope -Estimated: ~60 lines (down from 1044). +**`AGENT_TOOL_NAMES` is REDUNDANT — DELETE it (not just move).** It's a hardcoded snapshot of `mcp_tool_specs.tool_names()`. The existing test `test_tool_names_subset_of_models_agent_tool_names` literally asserts `tool_names() ⊆ AGENT_TOOL_NAMES`. Derive the list at consumer sites: `list(mcp_tool_specs.tool_names())`. Update 8 consumer sites (3 in `app_controller.py` + 5 in `tests/test_arch_boundary_phase2.py`). The cross-check test becomes either redundant or converts to a positive assertion that the set is derived correctly. + +Estimated: ~30 lines (down from 1044, down from 60 if you keep the redundant constant). ### Group D: KEEP AS-IS (the rest)