Class: Aidp::Harness::Runner
- Inherits:
-
Object
- Object
- Aidp::Harness::Runner
- Includes:
- MessageDisplay
- Defined in:
- lib/aidp/harness/runner.rb
Overview
Main harness runner that orchestrates the execution loop
Constant Summary collapse
- STATES =
Harness execution states
{ idle: "idle", running: "running", paused: "paused", waiting_for_user: "waiting_for_user", waiting_for_rate_limit: "waiting_for_rate_limit", stopped: "stopped", completed: "completed", error: "error", needs_clarification: "needs_clarification" }.freeze
Constants included from MessageDisplay
Instance Attribute Summary collapse
-
#clarification_questions ⇒ Object
readonly
Public accessors for testing and integration.
-
#current_provider ⇒ Object
readonly
Public accessors for testing and integration.
-
#current_step ⇒ Object
readonly
Public accessors for testing and integration.
-
#execution_log ⇒ Object
readonly
Public accessors for testing and integration.
-
#provider_manager ⇒ Object
readonly
Public accessors for testing and integration.
-
#user_input ⇒ Object
readonly
Public accessors for testing and integration.
Instance Method Summary collapse
-
#detailed_status ⇒ Object
Get detailed status including all components.
-
#initialize(project_dir, mode = :analyze, options = {}) ⇒ Runner
constructor
A new instance of Runner.
-
#pause ⇒ Object
Pause the harness execution.
-
#resume ⇒ Object
Resume the harness execution.
-
#run ⇒ Object
Main execution method - runs the harness loop.
-
#status ⇒ Object
Get current harness status.
-
#stop ⇒ Object
Stop the harness execution.
Methods included from MessageDisplay
#display_message, included, #message_display_prompt
Constructor Details
#initialize(project_dir, mode = :analyze, options = {}) ⇒ Runner
Returns a new instance of Runner.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/aidp/harness/runner.rb', line 38 def initialize(project_dir, mode = :analyze, = {}) @project_dir = project_dir @mode = mode.to_sym @options = @state = STATES[:idle] @start_time = nil @current_step = nil @current_provider = nil @user_input = [:user_input] || {} # Include user input from workflow selection @execution_log = [] @last_error = nil @prompt = [:prompt] || TTY::Prompt.new # Store workflow configuration @selected_steps = [:selected_steps] @workflow_type = [:workflow_type] @non_interactive = [:non_interactive] || (@workflow_type == :watch_mode) # Initialize components @configuration = Configuration.new(project_dir) @state_manager = StateManager.new(project_dir, @mode) @provider_manager = ProviderManager.new(@configuration, prompt: @prompt) # Use ZFC-enabled condition detector # ZfcConditionDetector will create its own ProviderFactory if needed # Falls back to legacy pattern matching when ZFC is disabled require_relative "zfc_condition_detector" @condition_detector = ZfcConditionDetector.new(@configuration) @user_interface = SimpleUserInterface.new @error_handler = ErrorHandler.new(@provider_manager, @configuration) @status_display = StatusDisplay.new @completion_checker = CompletionChecker.new(@project_dir, @workflow_type) @failure_reason = nil @failure_metadata = nil end |
Instance Attribute Details
#clarification_questions ⇒ Object (readonly)
Public accessors for testing and integration
36 37 38 |
# File 'lib/aidp/harness/runner.rb', line 36 def clarification_questions @clarification_questions end |
#current_provider ⇒ Object (readonly)
Public accessors for testing and integration
36 37 38 |
# File 'lib/aidp/harness/runner.rb', line 36 def current_provider @current_provider end |
#current_step ⇒ Object (readonly)
Public accessors for testing and integration
36 37 38 |
# File 'lib/aidp/harness/runner.rb', line 36 def current_step @current_step end |
#execution_log ⇒ Object (readonly)
Public accessors for testing and integration
36 37 38 |
# File 'lib/aidp/harness/runner.rb', line 36 def execution_log @execution_log end |
#provider_manager ⇒ Object (readonly)
Public accessors for testing and integration
36 37 38 |
# File 'lib/aidp/harness/runner.rb', line 36 def provider_manager @provider_manager end |
#user_input ⇒ Object (readonly)
Public accessors for testing and integration
36 37 38 |
# File 'lib/aidp/harness/runner.rb', line 36 def user_input @user_input end |
Instance Method Details
#detailed_status ⇒ Object
Get detailed status including all components
219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/aidp/harness/runner.rb', line 219 def detailed_status { harness: status, configuration: { default_provider: @configuration.default_provider, fallback_providers: @configuration.fallback_providers, max_retries: @configuration.harness_config[:max_retries] }, provider_manager: @provider_manager.status, error_stats: @error_handler.error_stats } end |
#pause ⇒ Object
Pause the harness execution
179 180 181 182 183 184 185 |
# File 'lib/aidp/harness/runner.rb', line 179 def pause return unless @state == STATES[:running] @state = STATES[:paused] log_execution("Harness paused by user") @status_display.show_paused_status end |
#resume ⇒ Object
Resume the harness execution
188 189 190 191 192 193 194 |
# File 'lib/aidp/harness/runner.rb', line 188 def resume return unless @state == STATES[:paused] @state = STATES[:running] log_execution("Harness resumed by user") @status_display.show_resumed_status end |
#run ⇒ Object
Main execution method - runs the harness loop
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/aidp/harness/runner.rb', line 76 def run @state = STATES[:running] @start_time = Time.now log_execution("Harness started", {mode: @mode, project_dir: @project_dir}) begin # Load existing state if resuming load_state if @state_manager.has_state? # Get the appropriate runner for the mode runner = get_mode_runner # Main execution loop loop do break if should_stop? # Check for pause conditions if should_pause? handle_pause_condition next end # Get next step to execute next_step = get_next_step(runner) break unless next_step # Execute the step execute_step(runner, next_step) # Update state update_state end # Mark as completed if we finished all steps AND all completion criteria are met if all_steps_completed?(runner) completion_status = @completion_checker.completion_status if completion_status[:all_complete] @state = STATES[:completed] log_execution("Harness completed successfully - all criteria met", completion_status) else log_execution("Steps completed but completion criteria not met", completion_status) ("\n⚠️ All steps completed but some completion criteria not met:", type: :warning) (completion_status[:summary], type: :info) # Ask user if they want to continue anyway if confirmation_prompt_allowed? if @user_interface.get_confirmation("Continue anyway? This may indicate issues that should be addressed.", default: false) @state = STATES[:completed] log_execution("Harness completed with user override") else mark_completion_failure(completion_status) @state = STATES[:error] log_execution("Harness stopped due to unmet completion criteria") end else ("⚠️ Non-interactive mode: cannot override failed completion criteria. Stopping run.", type: :warning) mark_completion_failure(completion_status) @state = STATES[:error] log_execution("Harness stopped due to unmet completion criteria in non-interactive mode") end end end rescue Aidp::Errors::ConfigurationError # Configuration errors should crash immediately (crash-early principle) # Re-raise without catching raise rescue => e @state = STATES[:error] @last_error = e log_execution("Harness error: #{e.}", {error: e.class.name, backtrace: e.backtrace&.first(5)}) handle_error(e) ensure # Save state before exiting - protect against exceptions during cleanup begin save_state rescue => e # Don't let state save failures kill the whole run or prevent cleanup Aidp.logger.error("harness", "Failed to save state during cleanup: #{e.}", error: e.class.name) @last_error ||= e # Only set if no previous error end begin cleanup rescue => e # Don't let cleanup failures propagate Aidp.logger.error("harness", "Failed during cleanup: #{e.}", error: e.class.name) end end result = {status: @state, message: } result[:reason] = @failure_reason if @failure_reason result[:failure_metadata] = @failure_metadata if @failure_metadata result[:clarification_questions] = @clarification_questions if @clarification_questions if @last_error result[:error] = @last_error. result[:error_class] = @last_error.class.name result[:backtrace] = @last_error.backtrace&.first(10) end result end |
#status ⇒ Object
Get current harness status
204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/aidp/harness/runner.rb', line 204 def status { state: @state, mode: @mode, current_step: @current_step, current_provider: @current_provider, start_time: @start_time, duration: @start_time ? Time.now - @start_time : 0, user_input_count: @user_input.size, execution_log_count: @execution_log.size, progress: @state_manager.progress_summary } end |
#stop ⇒ Object
Stop the harness execution
197 198 199 200 201 |
# File 'lib/aidp/harness/runner.rb', line 197 def stop @state = STATES[:stopped] log_execution("Harness stopped by user") @status_display.show_stopped_status end |