""" Refinement loop for iterative quality improvement. Implements FR-10: Halting and Refinement Policy Execute → Validate → Halt or Refine cycle. """ from typing import Callable, List, Optional, Tuple from markitect.prompts.quality.models import ( HaltDecision, QualityPolicy, RefinementResult, ValidationResult, ) from markitect.prompts.quality.policy import HaltingPolicyEngine from markitect.prompts.quality.validator import QualityValidator class RefinementLoop: """ Iterative refinement loop with quality gate checks. Executes a cycle of: execute → validate → check halting → refine until a halting condition is met. """ def __init__( self, validator: QualityValidator, policy: QualityPolicy, ): """ Initialize with validator and policy. Args: validator: Quality validator with configured gates policy: Halting policy configuration """ self.validator = validator self.policy = policy self.policy_engine = HaltingPolicyEngine(policy) def run( self, execution_callback: Callable[[int, List[ValidationResult]], Tuple[str, str, str]], artifact_id: str, ) -> RefinementResult: """ Execute the refinement loop. The execution_callback is called each iteration with: - iteration number (1-based) - previous validation results (empty list on first iteration) It should return a tuple of (run_id, content, artifact_id). Args: execution_callback: Callable that executes/refines and returns (run_id, content, artifact_id) artifact_id: ID of the artifact being refined Returns: RefinementResult with complete iteration history """ result = RefinementResult(iterations_run=0) score_history: List[float] = [] prev_results: List[ValidationResult] = [] for iteration in range(1, self.policy.max_iterations + 1): # Execute / refine run_id, content, art_id = execution_callback(iteration, prev_results) result.run_ids.append(run_id) # Validate current_results = self.validator.validate_artifact( content, art_id, run_id=run_id if self.validator.db_path else None, ) result.all_results.append(current_results) result.iterations_run = iteration # Evaluate halting halting_record = self.policy_engine.evaluate( results=current_results, iteration=iteration, score_history=score_history, total_runs=len(result.run_ids), ) current_score = self.policy_engine._aggregate_score(current_results) score_history.append(current_score) if halting_record.decision != HaltDecision.CONTINUE: result.final_results = current_results result.halting_record = halting_record return result prev_results = current_results # Reached max iterations without explicit halt result.final_results = prev_results result.halting_record = self.policy_engine.evaluate( results=prev_results, iteration=self.policy.max_iterations, score_history=score_history, total_runs=len(result.run_ids), ) return result