#!/usr/bin/env python3 """ Uni-Wizard Router v2 — Intelligent delegation across the three houses Routes tasks to the appropriate house based on task characteristics: - READ/ARCHIVE tasks → Ezra (archivist) - BUILD/TEST tasks → Bezalel (artificer) - JUDGE/REVIEW tasks → Timmy (sovereign) Usage: router = HouseRouter() result = router.route("read_and_summarize", {"repo": "timmy-home"}) """ import json from typing import Dict, Any, Optional, List from pathlib import Path from dataclasses import dataclass from enum import Enum from harness import UniWizardHarness, House, ExecutionResult class TaskType(Enum): """Categories of work for routing decisions""" READ = "read" # Read, analyze, summarize ARCHIVE = "archive" # Store, catalog, preserve SYNTHESIZE = "synthesize" # Combine, reconcile, interpret BUILD = "build" # Implement, create, construct TEST = "test" # Verify, validate, benchmark OPTIMIZE = "optimize" # Tune, improve, harden JUDGE = "judge" # Review, decide, approve ROUTE = "route" # Delegate, coordinate, dispatch @dataclass class RoutingDecision: """Record of why a task was routed to a house""" task_type: str primary_house: str confidence: float reasoning: str fallback_houses: List[str] class HouseRouter: """ Routes tasks to the appropriate wizard house. The router understands the canon: - Ezra reads and orders the pattern - Bezalel builds and unfolds the pattern - Timmy judges and preserves sovereignty """ # Task → House mapping ROUTING_TABLE = { # Read/Archive tasks → Ezra TaskType.READ: { "house": House.EZRA, "confidence": 0.95, "reasoning": "Archivist house: reading is Ezra's domain" }, TaskType.ARCHIVE: { "house": House.EZRA, "confidence": 0.95, "reasoning": "Archivist house: preservation is Ezra's domain" }, TaskType.SYNTHESIZE: { "house": House.EZRA, "confidence": 0.85, "reasoning": "Archivist house: synthesis requires reading first" }, # Build/Test tasks → Bezalel TaskType.BUILD: { "house": House.BEZALEL, "confidence": 0.95, "reasoning": "Artificer house: building is Bezalel's domain" }, TaskType.TEST: { "house": House.BEZALEL, "confidence": 0.95, "reasoning": "Artificer house: verification is Bezalel's domain" }, TaskType.OPTIMIZE: { "house": House.BEZALEL, "confidence": 0.90, "reasoning": "Artificer house: optimization is Bezalel's domain" }, # Judge/Route tasks → Timmy TaskType.JUDGE: { "house": House.TIMMY, "confidence": 1.0, "reasoning": "Sovereign house: judgment is Timmy's domain" }, TaskType.ROUTE: { "house": House.TIMMY, "confidence": 0.95, "reasoning": "Sovereign house: routing is Timmy's domain" }, } # Tool → TaskType mapping TOOL_TASK_MAP = { # System tools "system_info": TaskType.READ, "process_list": TaskType.READ, "service_status": TaskType.READ, "service_control": TaskType.BUILD, "health_check": TaskType.TEST, "disk_usage": TaskType.READ, # Git tools "git_status": TaskType.READ, "git_log": TaskType.ARCHIVE, "git_pull": TaskType.BUILD, "git_commit": TaskType.ARCHIVE, "git_push": TaskType.BUILD, "git_checkout": TaskType.BUILD, "git_branch_list": TaskType.READ, # Network tools "http_get": TaskType.READ, "http_post": TaskType.BUILD, "gitea_list_issues": TaskType.READ, "gitea_get_issue": TaskType.READ, "gitea_create_issue": TaskType.BUILD, "gitea_comment": TaskType.BUILD, } def __init__(self): self.harnesses: Dict[House, UniWizardHarness] = { House.TIMMY: UniWizardHarness("timmy"), House.EZRA: UniWizardHarness("ezra"), House.BEZALEL: UniWizardHarness("bezalel") } self.decision_log: List[RoutingDecision] = [] def classify_task(self, tool_name: str, params: Dict) -> TaskType: """Classify a task based on tool and parameters""" # Direct tool mapping if tool_name in self.TOOL_TASK_MAP: return self.TOOL_TASK_MAP[tool_name] # Heuristic classification if any(kw in tool_name for kw in ["read", "get", "list", "status", "info", "log"]): return TaskType.READ if any(kw in tool_name for kw in ["write", "create", "commit", "push", "post"]): return TaskType.BUILD if any(kw in tool_name for kw in ["test", "check", "verify", "validate"]): return TaskType.TEST # Default to Timmy for safety return TaskType.ROUTE def route(self, tool_name: str, **params) -> ExecutionResult: """ Route a task to the appropriate house and execute. Returns execution result with routing metadata attached. """ # Classify the task task_type = self.classify_task(tool_name, params) # Get routing decision routing = self.ROUTING_TABLE.get(task_type, { "house": House.TIMMY, "confidence": 0.5, "reasoning": "Default to sovereign house" }) house = routing["house"] # Record decision decision = RoutingDecision( task_type=task_type.value, primary_house=house.value, confidence=routing["confidence"], reasoning=routing["reasoning"], fallback_houses=[h.value for h in [House.TIMMY] if h != house] ) self.decision_log.append(decision) # Execute via the chosen harness harness = self.harnesses[house] result = harness.execute(tool_name, **params) # Attach routing metadata result.data = { "result": result.data, "routing": { "task_type": task_type.value, "house": house.value, "confidence": routing["confidence"], "reasoning": routing["reasoning"] } } return result def execute_multi_house_plan( self, plan: List[Dict], require_timmy_approval: bool = False ) -> Dict[str, Any]: """ Execute a plan that may span multiple houses. Example plan: [ {"tool": "git_status", "params": {}, "house": "ezra"}, {"tool": "git_commit", "params": {"message": "Update"}, "house": "ezra"}, {"tool": "git_push", "params": {}, "house": "bezalel"} ] """ results = {} ezra_review = None bezalel_proof = None for step in plan: tool_name = step.get("tool") params = step.get("params", {}) specified_house = step.get("house") # Use specified house or auto-route if specified_house: harness = self.harnesses[House(specified_house)] result = harness.execute(tool_name, **params) else: result = self.route(tool_name, **params) results[tool_name] = result # Collect review/proof for Timmy if specified_house == "ezra": ezra_review = result elif specified_house == "bezalel": bezalel_proof = result # If required, get Timmy's approval if require_timmy_approval: timmy_harness = self.harnesses[House.TIMMY] # Build review package review_input = { "ezra_work": { "success": ezra_review.success if ezra_review else None, "evidence_level": ezra_review.provenance.evidence_level if ezra_review else None, "sources": ezra_review.provenance.sources_read if ezra_review else [] }, "bezalel_work": { "success": bezalel_proof.success if bezalel_proof else None, "proof_verified": bezalel_proof.success if bezalel_proof else None } if bezalel_proof else None } # Timmy judges timmy_result = timmy_harness.execute( "review_proposal", proposal=json.dumps(review_input) ) results["timmy_judgment"] = timmy_result return results def get_routing_stats(self) -> Dict: """Get statistics on routing decisions""" if not self.decision_log: return {"total": 0} by_house = {} by_task = {} total_confidence = 0 for d in self.decision_log: by_house[d.primary_house] = by_house.get(d.primary_house, 0) + 1 by_task[d.task_type] = by_task.get(d.task_type, 0) + 1 total_confidence += d.confidence return { "total": len(self.decision_log), "by_house": by_house, "by_task_type": by_task, "avg_confidence": round(total_confidence / len(self.decision_log), 2) } class CrossHouseWorkflow: """ Pre-defined workflows that coordinate across houses. Implements the canonical flow: 1. Ezra reads and shapes 2. Bezalel builds and proves 3. Timmy reviews and approves """ def __init__(self): self.router = HouseRouter() def issue_to_pr_workflow(self, issue_number: int, repo: str) -> Dict: """ Full workflow: Issue → Ezra analysis → Bezalel implementation → Timmy review """ workflow_id = f"issue_{issue_number}" # Phase 1: Ezra reads and shapes the issue ezra_harness = self.router.harnesses[House.EZRA] issue_data = ezra_harness.execute("gitea_get_issue", repo=repo, number=issue_number) if not issue_data.success: return { "workflow_id": workflow_id, "phase": "ezra_read", "status": "failed", "error": issue_data.error } # Phase 2: Ezra synthesizes approach # (Would call LLM here in real implementation) approach = { "files_to_modify": ["file1.py", "file2.py"], "tests_needed": True } # Phase 3: Bezalel implements bezalel_harness = self.router.harnesses[House.BEZALEL] # Execute implementation plan # Phase 4: Bezalel proves with tests test_result = bezalel_harness.execute("run_tests", repo_path=repo) # Phase 5: Timmy reviews timmy_harness = self.router.harnesses[House.TIMMY] review = timmy_harness.review_for_timmy({ "ezra_analysis": issue_data, "bezalel_implementation": test_result }) return { "workflow_id": workflow_id, "status": "complete", "phases": { "ezra_read": issue_data.success, "bezalel_implement": test_result.success, "timmy_review": review }, "recommendation": review.get("recommendation", "PENDING") } if __name__ == "__main__": print("=" * 60) print("HOUSE ROUTER — Three-House Delegation Demo") print("=" * 60) router = HouseRouter() # Demo routing decisions demo_tasks = [ ("git_status", {"repo_path": "/tmp/timmy-home"}), ("git_commit", {"repo_path": "/tmp/timmy-home", "message": "Test"}), ("system_info", {}), ("health_check", {}), ] print("\n📋 Task Routing Decisions:") print("-" * 60) for tool, params in demo_tasks: task_type = router.classify_task(tool, params) routing = router.ROUTING_TABLE.get(task_type, {}) print(f"\n Tool: {tool}") print(f" Task Type: {task_type.value}") print(f" Routed To: {routing.get('house', House.TIMMY).value}") print(f" Confidence: {routing.get('confidence', 0.5)}") print(f" Reasoning: {routing.get('reasoning', 'Default')}") print("\n" + "=" * 60) print("Routing complete.")