refactor(agents): Implement thin orchestrator pattern for Design Assistant#53
Conversation
…stant Splits the monolithic design_assistant.py (~1030 lines) into a modular architecture: - DesignOrchestrator: Thin routing layer that handles phase transitions only - BasePhaseAgent: Abstract base class with common hook handling for UI events - GoalUnderstandingAgent: Phase 1 - goal discovery and confirmation - AgentConfigurationAgent: Phase 2 - specialist agent design - BlueprintDesignAgent: Phase 3 - interactive blueprint creation Benefits: - Each phase agent owns its prompt, tools, and transition logic - Shared session state via existing tools.py mechanism - Backward compatible - DesignAssistantSession wraps the orchestrator - Reduced orchestrator complexity from ~850 to ~100 lines 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Code Review: Thin Orchestrator Pattern RefactorThis is a well-executed refactoring that significantly improves the architecture. Net deletion of 849 lines while adding structured functionality. Strengths
Critical Issues (must fix before merge)1. Missing Abstract Method (base.py:67-76)get_description() implemented by subclasses but not defined in BasePhaseAgent 2. Phase Transition Error Handling (orchestrator.py:225-231)Invalid phase silently ignored, could cause infinite loops 3. Missing Integration TestsFrontend integration tests not complete per PR description High Priority Issues4. Prompt Hydration (agent_configuration.py:45-60, blueprint_design.py:55-78)Naive string replacement, no validation 5. Event Queue Race (orchestrator.py:369-375)TOCTOU race in empty() check then get_nowait() 6. Hook Delegation (orchestrator.py:157-183)Accessing protected _pre_tool_hook from outside Medium Priority (follow-up PR)
Minor Issues
Great architectural work! Address critical issues before merge. |
…ger into orchestrator - Delete design_assistant.py legacy wrapper module - Move SessionManager class and session_manager singleton to orchestrator.py - Update imports in main.py and design_sessions.py to use orchestrator module - No functional changes, just module consolidation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Code Review: Thin Orchestrator Pattern RefactoringThis is an excellent architectural refactoring that significantly improves maintainability and follows solid software engineering principles. The code quality is high overall, with a few areas for improvement. 🎯 Overall AssessmentAPPROVED with minor recommendations Strengths
📋 Detailed Feedback1. Architecture & Design ⭐⭐⭐⭐⭐The thin orchestrator pattern is well-implemented:
Recommendation: Consider documenting the state synchronization flow between tool state and session state more explicitly, as this is a critical aspect of the architecture. 2. Code Quality ⭐⭐⭐⭐Good Practices:
Areas for Improvement:a) Orchestrator Hook Delegation (orchestrator.py:162-188) async def pre_tool_hook(input_data, tool_use_id, context):
agent = self._get_phase_agent(self.state.phase)
return await agent._pre_tool_hook(input_data, tool_use_id, context)Issue: This calls the "private" method Suggestion: Make b) BasePhaseAgent._ui_emitted_in_turn (base.py:64, 134) Question: Is this used elsewhere, or can it be removed? If it's for future use, add a TODO comment explaining the intent. c) Error Handling in Orchestrator (orchestrator.py:340-427) async def send_message(self, message: str) -> AsyncIterator[AGUIEvent]:
# ... no try/except around SDK calls
await self._client.query(prompt=actual_message)
async for msg in self._client.receive_response():
# ...Risk: If the SDK client raises an exception during streaming, it could leave the session in an inconsistent state. Recommendation: Add try/except around the SDK interaction with proper cleanup and error event emission: try:
await self._client.query(prompt=actual_message)
async for msg in self._client.receive_response():
# ... existing logic
except Exception as e:
logger.exception(f"[{self.session_id}] Error during message processing")
yield AGUIEvent(type="ERROR", data={"message": str(e)})
# Re-sync state after error
self._sync_state_from_tools()3. Potential Bugs
|
Summary
Refactors the monolithic
design_assistant.py(~1030 lines) into a clean, modular architecture following the thin orchestrator pattern:Key Benefits
tools.pymechanismDesignAssistantSessionwraps the orchestratorFiles Changed
docs/design-thin-orchestrator.md- Architecture design documentsrc/backend/clara/agents/orchestrator.py- New thin orchestratorsrc/backend/clara/agents/phase_agents/- New directory with:base.py- BasePhaseAgent abstract classgoal_understanding.py- Phase 1 agentagent_configuration.py- Phase 2 agentblueprint_design.py- Phase 3 agentsrc/backend/clara/agents/design_assistant.py- Slimmed down wrapperTest plan
🤖 Generated with Claude Code