Private
Public Access
0
0
Files
manual_slop/conductor/tracks/refactor_oop.md
T
2026-06-06 00:42:40 -04:00

4.3 KiB

OOP Refactoring Tracker

Tracks elimination of class-based OOP from the codebase to reduce AI agent scope misinterpretation.

Status: IN PROGRESS (UI Delegation Pattern widely adopted; core classes retained with justification)

Phase 1: Leaf Classes (No internal dependents)

These classes have no dependencies on other classes being refactored. Start here.

  • src/tool_bias.py - ToolBiasEngine → module functions + dict
  • src/session_logger.py - SessionLogger → module-level functions
  • src/summarize.py - SummaryCache class → plain dict + functions

Phase 2: Internal Classes (Depend on Phase 1)

These classes use Phase 1 classes. Refactor Phase 1 first.

  • src/project_manager.py - ProjectManager class
  • src/aggregate.py - Aggregator class

Phase 3: Core Classes (High risk - many callers)

These have many dependents. Handle last with extra testing.

  • src/ai_client.py - AI client classes (AIProvider, etc.) — justified: holds multi-provider singleton state
  • src/mcp_client.py - MCPClient, ToolRegistry — justified: tool dispatch handler map + 3-layer security state
  • src/models.py - Data classes → dataclass/NamedTuple — already done: uses pydantic + dataclass(frozen=True)

Phase 4: GUI Classes (Highest risk)

Classes used in GUI callbacks. Require live testing.

  • src/gui_2.py - DELEGATION EXTRACTED: ~90 module-level render_xxx(app) functions, App class retains only lifecycle and thin _render_xxx(self) wrappers. See docs/guide_gui_2.md §"UI Delegation Pattern".
  • src/command_palette.py - EXTRACTED: CommandRegistry and render_palette_modal are module-level. Command dataclass is data-only. See docs/guide_command_palette.md.
  • src/commands.py - EXTRACTED: All 32 commands registered via @registry.register decorator at module level.

Anti-Regression Protocol

Before refactoring ANY class:

  1. Write test that validates current behavior
  2. Commit baseline with test(baseline): add baseline for <class>
  3. Extract method into module-level function
  4. Update all callers to use function directly
  5. Run test - must pass identically
  6. Commit extraction with refactor(oop): extract <method> from <Class>
  7. Delete class only when ALL methods extracted

Progress Log

2026-05-11

  • Initial tracker created
  • Anti-OOP conventions added to conductor/code_styleguides/python.md
  • Ruff PLR rules added to pyproject.toml

2026-06-02

  • src/gui_2.py UI Delegation Pattern fully extracted: ~90 module-level render_xxx(app) functions. App class reduced to thin wrappers.
  • src/command_palette.py extracted: CommandRegistry is a singleton class holding a dict (justified: needs encapsulated mutable state for register/unregister). All other logic is module-level.
  • src/commands.py extracted: All 32 commands are module-level functions registered via @registry.register. Two helper functions (_toggle_window, _toggle_attr) are module-level.
  • src/models.py confirmed using pydantic + dataclass(frozen=True) — already follows data-only container pattern. No changes needed.
  • Hot Reload support verified: UI Delegation Pattern enables HotReloader to swap gui_2.render_xxx references at runtime without losing app: App state.

Pending Refactors (Out of Scope, tracked here)

  • src/tool_bias.py::ToolBiasEngine — small class (~50 lines), refactor candidate but currently 1+ methods.
  • src/session_logger.py::SessionLogger — facade class, refactor candidate.
  • src/summarize.py::SummaryCache — class wraps LRU dict + lock, refactor candidate but lock encapsulation is a valid justification.
  • src/ai_client.py::AIProvider subclasses — abstract base with 5 concrete subclasses. Refactor would require feature-flag dispatch, not worth the churn.
  • src/mcp_client.py::MCPClient — would split into dispatch (function) + _state (module globals). See docs/guide_mcp_client.md §"dispatch" for current structure.

Notes

  • Use Strangler Fig pattern: keep class working until last caller migrated
  • Prefer dict/NamedTuple over classes for data containers
  • Classes with only data: convert to dataclass(frozen=True) or NamedTuple