Class: Aidp::Execute::WorkLoopRunner

Inherits:
Object
  • Object
show all
Includes:
MessageDisplay
Defined in:
lib/aidp/execute/work_loop_runner.rb

Overview

Executes work loops for a single step using the fix-forward pattern Responsibilities:

  • Create initial PROMPT.md from templates and context

  • Loop: send PROMPT.md to agent, run tests/linters, check completion

  • Only send test/lint failures back to agent

  • Never rollback, only move forward through fixes

  • Track iteration count and state transitions

  • Record periodic checkpoints with metrics

Fix-Forward State Machine: READY → APPLY_PATCH → TEST → → DONE | FAIL → DIAGNOSE → NEXT_PATCH → READY

Constant Summary collapse

STATES =

State machine states

{
  ready: "READY",               # Ready to start new iteration
  apply_patch: "APPLY_PATCH",   # Agent applying changes
  test: "TEST",                 # Running tests and linters
  pass: "PASS",                 # Tests passed
  fail: "FAIL",                 # Tests failed
  diagnose: "DIAGNOSE",         # Analyzing failures
  next_patch: "NEXT_PATCH",     # Preparing next iteration
  done: "DONE"                  # Work complete
}.freeze
MAX_ITERATIONS =

Safety limit

50
CHECKPOINT_INTERVAL =

Record checkpoint every N iterations

5
STYLE_GUIDE_REMINDER_INTERVAL =

Re-inject LLM_STYLE_GUIDE every N iterations

5

Constants included from MessageDisplay

MessageDisplay::COLOR_MAP

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from MessageDisplay

#display_message, included, #message_display_prompt

Constructor Details

#initialize(project_dir, provider_manager, config, options = {}) ⇒ WorkLoopRunner

Returns a new instance of WorkLoopRunner.



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
# File 'lib/aidp/execute/work_loop_runner.rb', line 47

def initialize(project_dir, provider_manager, config, options = {})
  @project_dir = project_dir
  @provider_manager = provider_manager
  @config = config
  @prompt = options[:prompt] || TTY::Prompt.new
  @prompt_manager = PromptManager.new(project_dir, config: config)
  @test_runner = Aidp::Harness::TestRunner.new(project_dir, config)
  @checkpoint = Checkpoint.new(project_dir)
  @checkpoint_display = CheckpointDisplay.new(prompt: @prompt)
  @guard_policy = GuardPolicy.new(project_dir, config.guards_config)
  @persistent_tasklist = PersistentTasklist.new(project_dir)
  @iteration_count = 0
  @step_name = nil
  @options = options
  @current_state = :ready
  @state_history = []
  @deterministic_runner = DeterministicUnits::Runner.new(project_dir)
  @unit_scheduler = nil

  # Initialize thinking depth manager for intelligent model selection
  require_relative "../harness/thinking_depth_manager"
  @thinking_depth_manager = options[:thinking_depth_manager] || Aidp::Harness::ThinkingDepthManager.new(config)
  @consecutive_failures = 0
  @last_tier = nil
end

Instance Attribute Details

#current_stateObject (readonly)

Returns the value of attribute current_state.



41
42
43
# File 'lib/aidp/execute/work_loop_runner.rb', line 41

def current_state
  @current_state
end

#iteration_countObject (readonly)

Returns the value of attribute iteration_count.



41
42
43
# File 'lib/aidp/execute/work_loop_runner.rb', line 41

def iteration_count
  @iteration_count
end

#project_dirObject (readonly)

Returns the value of attribute project_dir.



41
42
43
# File 'lib/aidp/execute/work_loop_runner.rb', line 41

def project_dir
  @project_dir
end

Instance Method Details

#execute_step(step_name, step_spec, context = {}) ⇒ Object

Execute a step using fix-forward work loop pattern Returns final result when step is complete Never rolls back - only moves forward through fixes



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
# File 'lib/aidp/execute/work_loop_runner.rb', line 76

def execute_step(step_name, step_spec, context = {})
  @step_name = step_name
  @iteration_count = 0
  transition_to(:ready)

  Aidp.logger.info("work_loop", "Starting hybrid work loop execution", step: step_name, max_iterations: MAX_ITERATIONS)

  display_message("🔄 Starting hybrid work loop for step: #{step_name}", type: :info)
  display_message("  Flow: Deterministic ↔ Agentic with fix-forward core", type: :info)
  display_work_context(step_name, context)

  display_guard_policy_status
  display_pending_tasks

  @unit_scheduler = WorkLoopUnitScheduler.new(units_config, project_dir: @project_dir)
  base_context = context.dup

  loop do
    unit = @unit_scheduler.next_unit
    break unless unit

    if unit.deterministic?
      result = @deterministic_runner.run(unit.definition, reason: "scheduled by work loop")
      @unit_scheduler.record_deterministic_result(unit.definition, result)
      next
    end

    enriched_context = base_context.merge(
      deterministic_outputs: @unit_scheduler.deterministic_context,
      previous_agent_summary: @unit_scheduler.last_agentic_summary
    )

    agentic_payload = if unit.name == :decide_whats_next
      run_decider_agentic_unit(enriched_context)
    elsif unit.name == :diagnose_failures
      run_diagnose_agentic_unit(enriched_context)
    else
      run_primary_agentic_unit(step_spec, enriched_context)
    end

    @unit_scheduler.record_agentic_result(
      agentic_payload[:raw_result] || {},
      requested_next: agentic_payload[:requested_next],
      summary: agentic_payload[:summary],
      completed: agentic_payload[:completed]
    )

    return agentic_payload[:response] if agentic_payload[:terminate]
  end

  build_max_iterations_result
end