Class: Aidp::Harness::EnhancedRunner

Inherits:
Object
  • Object
show all
Defined in:
lib/aidp/harness/enhanced_runner.rb

Overview

Enhanced harness runner with modern TTY-based TUI

Defined Under Namespace

Classes: Sleeper

Constant Summary collapse

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"
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(project_dir, mode = :analyze, options = {}, prompt: TTY::Prompt.new, sleeper: Sleeper.new) ⇒ EnhancedRunner

Returns a new instance of EnhancedRunner.



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
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/aidp/harness/enhanced_runner.rb', line 42

def initialize(project_dir, mode = :analyze, options = {}, prompt: TTY::Prompt.new, sleeper: Sleeper.new)
  @project_dir = project_dir
  @mode = mode.to_sym
  @options = options
  @prompt = prompt
  @sleeper = sleeper
  @state = STATES[:idle]
  @start_time = nil
  @current_step = nil
  @current_provider = nil
  @user_input = options[:user_input] || {}
  @user_input = {} if @user_input.nil? # Ensure it's never nil
  @execution_log = []

  # Store workflow configuration
  @selected_steps = options[:selected_steps] || []
  @workflow_type = options[:workflow_type] || :default

  # Initialize enhanced TUI components (with dependency injection)
  @tui = options[:tui] || UI::EnhancedTUI.new
  @workflow_selector = options[:workflow_selector] || UI::EnhancedWorkflowSelector.new(@tui)
  @job_monitor = options[:job_monitor] || UI::JobMonitor.new
  @workflow_controller = options[:workflow_controller] || UI::WorkflowController.new
  @progress_display = options[:progress_display] || UI::ProgressDisplay.new
  @status_widget = options[:status_widget] || UI::StatusWidget.new

  # Initialize other components (with dependency injection)
  @configuration = options[:configuration] || Configuration.new(project_dir)
  @state_manager = options[:state_manager] || StateManager.new(project_dir, @mode)
  @provider_manager = options[: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
  if options[:condition_detector]
    @condition_detector = options[:condition_detector]
  else
    require_relative "zfc_condition_detector"
    @condition_detector = ZfcConditionDetector.new(@configuration)
  end

  @error_handler = options[:error_handler] || ErrorHandler.new(@provider_manager, @configuration)
  @completion_checker = options[:completion_checker] || CompletionChecker.new(@project_dir, @workflow_type)
end

Instance Attribute Details

#completion_checker=(value) ⇒ Object (writeonly)

Sets the attribute completion_checker

Parameters:

  • value

    the value to set the attribute completion_checker to.



31
32
33
# File 'lib/aidp/harness/enhanced_runner.rb', line 31

def completion_checker=(value)
  @completion_checker = value
end

#condition_detectorObject

Returns the value of attribute condition_detector.



32
33
34
# File 'lib/aidp/harness/enhanced_runner.rb', line 32

def condition_detector
  @condition_detector
end

#configuration=(value) ⇒ Object (writeonly)

Sets the attribute configuration

Parameters:

  • value

    the value to set the attribute configuration to.



33
34
35
# File 'lib/aidp/harness/enhanced_runner.rb', line 33

def configuration=(value)
  @configuration = value
end

#current_providerObject

Get current provider (delegate to provider manager with fallback)



88
89
90
# File 'lib/aidp/harness/enhanced_runner.rb', line 88

def current_provider
  @current_provider || @provider_manager&.current_provider || "unknown"
end

#current_stepObject

Expose state for testability



27
28
29
# File 'lib/aidp/harness/enhanced_runner.rb', line 27

def current_step
  @current_step
end

#error_handler=(value) ⇒ Object (writeonly)

Sets the attribute error_handler

Parameters:

  • value

    the value to set the attribute error_handler to.



31
32
33
# File 'lib/aidp/harness/enhanced_runner.rb', line 31

def error_handler=(value)
  @error_handler = value
end

#execution_logObject

Get execution log (with nil safety)



98
99
100
# File 'lib/aidp/harness/enhanced_runner.rb', line 98

def execution_log
  @execution_log || []
end

#modeObject

Returns the value of attribute mode.



29
30
31
# File 'lib/aidp/harness/enhanced_runner.rb', line 29

def mode
  @mode
end

#project_dirObject

Returns the value of attribute project_dir.



29
30
31
# File 'lib/aidp/harness/enhanced_runner.rb', line 29

def project_dir
  @project_dir
end

#provider_managerObject (readonly)

Returns the value of attribute provider_manager.



30
31
32
# File 'lib/aidp/harness/enhanced_runner.rb', line 30

def provider_manager
  @provider_manager
end

#selected_stepsObject

Returns the value of attribute selected_steps.



29
30
31
# File 'lib/aidp/harness/enhanced_runner.rb', line 29

def selected_steps
  @selected_steps
end

#sleeperObject (readonly)

Returns the value of attribute sleeper.



30
31
32
# File 'lib/aidp/harness/enhanced_runner.rb', line 30

def sleeper
  @sleeper
end

#start_timeObject

Expose state for testability



27
28
29
# File 'lib/aidp/harness/enhanced_runner.rb', line 27

def start_time
  @start_time
end

#stateObject

Expose state for testability



27
28
29
# File 'lib/aidp/harness/enhanced_runner.rb', line 27

def state
  @state
end

#state_manager=(value) ⇒ Object (writeonly)

Sets the attribute state_manager

Parameters:

  • value

    the value to set the attribute state_manager to.



33
34
35
# File 'lib/aidp/harness/enhanced_runner.rb', line 33

def state_manager=(value)
  @state_manager = value
end

#user_inputObject

Get user input (with nil safety)



93
94
95
# File 'lib/aidp/harness/enhanced_runner.rb', line 93

def user_input
  @user_input || {}
end

#workflow_controller=(value) ⇒ Object (writeonly)

Sets the attribute workflow_controller

Parameters:

  • value

    the value to set the attribute workflow_controller to.



31
32
33
# File 'lib/aidp/harness/enhanced_runner.rb', line 31

def workflow_controller=(value)
  @workflow_controller = value
end

#workflow_typeObject

Returns the value of attribute workflow_type.



29
30
31
# File 'lib/aidp/harness/enhanced_runner.rb', line 29

def workflow_type
  @workflow_type
end

Instance Method Details

#complete_workflow_jobObject



333
334
335
336
337
338
339
# File 'lib/aidp/harness/enhanced_runner.rb', line 333

def complete_workflow_job
  @tui.update_job("main_workflow", {
    status: :completed,
    progress: 100,
    message: "Workflow completed"
  })
end

#execute_step_with_enhanced_tui(runner, step_name) ⇒ Object

Enhanced step execution with TUI integration



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/aidp/harness/enhanced_runner.rb', line 185

def execute_step_with_enhanced_tui(runner, step_name)
  @current_step = step_name
  @tui.show_message("πŸ”„ Executing step: #{step_name}", :info)

  # Register step as a job
  step_job_id = "step_#{step_name}"
  step_job_data = {
    name: step_name,
    status: :running,
    progress: 0,
    provider: @current_provider || "unknown",
    message: "Starting execution..."
  }
  @tui.add_job(step_job_id, step_job_data)

  # Show step execution display
  @tui.show_step_execution(step_name, :starting, {provider: @current_provider})

  # Mark step as in progress
  runner.mark_step_in_progress(step_name)

  # Get current provider
  @current_provider = @provider_manager.current_provider

  # Execute the step with error handling and spinner
  start_time = Time.now

  # Show spinner while executing the step
  spinner_message = "Executing #{step_name}..."
  result = show_step_spinner(spinner_message) do
    @error_handler.execute_with_retry do
      step_options = @options.merge(user_input: @user_input)
      # Determine execution directory (workstream path if set)
      exec_dir = begin
        if @state_manager.respond_to?(:current_workstream_path)
          @state_manager.current_workstream_path
        else
          @project_dir
        end
      rescue
        @project_dir
      end
      # Execute step within the chosen directory for proper isolation
      Dir.chdir(exec_dir) do
        runner.run_step(step_name, step_options)
      end
    end
  end
  duration = Time.now - start_time

  # Update step job status
  if result && result[:status] == "completed"
    @tui.update_job(step_job_id, {
      status: :completed,
      progress: 100,
      message: "Completed successfully"
    })
    @tui.show_step_execution(step_name, :completed, {duration: duration})
    runner.mark_step_completed(step_name)
  else
    @tui.update_job(step_job_id, {
      status: :failed,
      message: result&.dig(:error) || "Step execution failed"
    })
    @tui.show_step_execution(step_name, :failed, {
      error: result&.dig(:error) || "Unknown error"
    })
  end

  # Check for conditions that require user interaction
  if @condition_detector.needs_user_feedback?(result)
    handle_user_feedback_request_with_tui(result)
  end

  # Check for rate limiting
  if @condition_detector.is_rate_limited?(result)
    handle_rate_limit(result)
  end

  # Remove job after a delay to show completion
  # UI delay to let user see completion status before removal
  Thread.new do
    @sleeper.sleep(2) # UI timing delay
    @tui.remove_job(step_job_id)
  end

  result
end

#handle_user_feedback_request_with_tui(result) ⇒ Object

Enhanced user feedback handling



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/aidp/harness/enhanced_runner.rb', line 275

def handle_user_feedback_request_with_tui(result)
  @state = STATES[:waiting_for_user]
  @workflow_controller.pause_workflow("Waiting for user feedback")
  @tui.show_message("⏸️ Waiting for user feedback", :warning)

  # Extract questions from result
  questions = @condition_detector.extract_questions(result)

  # Show input area
  @tui.show_input_area("Please provide feedback:")

  # Collect user input using enhanced TUI
  user_responses = {}
  questions.each_with_index do |question_data, index|
    question_number = question_data[:number] || (index + 1)
    prompt = "Question #{question_number}: #{question_data[:question]}"

    response = @tui.get_user_input(prompt)
    user_responses["question_#{question_number}"] = response
  end

  # Store user input
  @user_input.merge!(user_responses)
  user_responses.each do |key, value|
    @state_manager.add_user_input(key, value)
  end

  @state = STATES[:running]
  @workflow_controller.resume_workflow("User feedback collected")
  @tui.show_message("βœ… User feedback collected", :success)
end

#register_workflow_jobObject

Job monitoring integration



321
322
323
324
325
326
327
328
329
330
331
# File 'lib/aidp/harness/enhanced_runner.rb', line 321

def register_workflow_job
  job_data = {
    name: "Main Workflow",
    status: :running,
    progress: 0,
    provider: @current_provider || "unknown",
    message: "Starting workflow execution..."
  }

  @tui.add_job("main_workflow", job_data)
end

#runObject

Main execution method with enhanced TUI



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
177
178
179
180
181
182
# File 'lib/aidp/harness/enhanced_runner.rb', line 103

def run
  @state = STATES[:running]
  @start_time = Time.now

  Aidp.logger.info("harness_runner", "Starting harness execution", mode: @mode, workflow_type: @workflow_type, steps_count: @selected_steps.size)

  @tui.show_message("πŸš€ Starting #{@mode.to_s.capitalize} Mode", :info)

  begin
    # Load existing state if resuming
    # Temporarily disabled to test
    # load_state if @state_manager.has_state?

    # Get the appropriate runner for the mode
    runner = get_mode_runner

    # Register main workflow job
    register_workflow_job

    # Show initial workflow status
    show_workflow_status(runner)

    # Show mode-specific feedback
    show_mode_specific_feedback

    # 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 with spinner
      next_step = show_step_spinner("Finding next step to execute...") do
        get_next_step(runner)
      end
      break unless next_step

      # Execute the step with enhanced TUI integration
      execute_step_with_enhanced_tui(runner, next_step)

      # Update state
      update_state
    end

    # Mark workflow as completed
    complete_workflow_job

    # Check completion criteria
    if all_steps_completed?(runner)
      completion_status = @completion_checker.completion_status
      if completion_status[:all_complete]
        @state = STATES[:completed]
        @workflow_controller.complete_workflow("All steps completed successfully")
        @tui.show_message("πŸŽ‰ Harness completed successfully - all criteria met", :success)
      else
        @tui.show_message("⚠️ Steps completed but completion criteria not met", :warning)
        handle_completion_criteria_not_met(completion_status)
      end
    end
  rescue Aidp::Errors::ConfigurationError
    # Configuration errors should crash immediately (crash-early principle)
    # Re-raise without catching
    raise
  rescue => e
    @state = STATES[:error]
    # Single error message - don't duplicate
    @tui.show_message("❌ Error: #{e.message}", :error)
  ensure
    # Save state before exiting
    save_state
    @tui.restore_screen
    cleanup
  end

  {status: @state, message: get_completion_message}
end

#show_workflow_status(runner) ⇒ Object

Enhanced workflow status display



308
309
310
311
312
313
314
315
316
317
318
# File 'lib/aidp/harness/enhanced_runner.rb', line 308

def show_workflow_status(runner)
  workflow_data = {
    workflow_type: @workflow_type,
    steps: @selected_steps || runner.all_steps,
    completed_steps: runner.progress.completed_steps.size,
    current_step: runner.progress.current_step,
    progress_percentage: calculate_progress_percentage(runner)
  }

  @tui.show_workflow_status(workflow_data)
end

#statusObject

Status methods



350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/aidp/harness/enhanced_runner.rb', line 350

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,
    jobs_count: @tui.jobs.size
  }
end

#stopObject

Control methods



343
344
345
346
347
# File 'lib/aidp/harness/enhanced_runner.rb', line 343

def stop
  @state = STATES[:stopped]
  @workflow_controller.stop_workflow("User requested stop")
  @tui.show_message("⏹️ Harness stopped by user", :warning)
end