Class: Aidp::Execute::WorkLoopRunner
- Inherits:
-
Object
- Object
- Aidp::Execute::WorkLoopRunner
- 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, MessageDisplay::CRITICAL_TYPES
Instance Attribute Summary collapse
-
#checkpoint ⇒ Object
readonly
Returns the value of attribute checkpoint.
-
#current_state ⇒ Object
readonly
Returns the value of attribute current_state.
-
#guard_policy ⇒ Object
writeonly
Sets the attribute guard_policy.
-
#iteration_count ⇒ Object
Expose state for testability.
-
#options ⇒ Object
Expose state for testability.
-
#persistent_tasklist ⇒ Object
Expose state for testability.
-
#project_dir ⇒ Object
readonly
Returns the value of attribute project_dir.
-
#prompt_manager ⇒ Object
Returns the value of attribute prompt_manager.
-
#state_history ⇒ Object
readonly
Returns the value of attribute state_history.
-
#step_name ⇒ Object
Expose state for testability.
-
#style_guide_selector ⇒ Object
writeonly
Sets the attribute style_guide_selector.
-
#test_runner ⇒ Object
readonly
Returns the value of attribute test_runner.
Instance Method Summary collapse
-
#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.
-
#initialize(project_dir, provider_manager, config, options = {}) ⇒ WorkLoopRunner
constructor
A new instance of WorkLoopRunner.
Methods included from MessageDisplay
#display_message, included, #message_display_prompt, #quiet_mode?
Constructor Details
#initialize(project_dir, provider_manager, config, options = {}) ⇒ WorkLoopRunner
Returns a new instance of WorkLoopRunner.
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 86 87 |
# File 'lib/aidp/execute/work_loop_runner.rb', line 53 def initialize(project_dir, provider_manager, config, = {}) @project_dir = project_dir @provider_manager = provider_manager @config = config @prompt = [: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) @work_context = {} @persistent_tasklist = PersistentTasklist.new(project_dir) @iteration_count = 0 @step_name = nil @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 = [:thinking_depth_manager] || Aidp::Harness::ThinkingDepthManager.new(config, root_dir: @project_dir) @consecutive_failures = 0 @last_tier = nil # Initialize style guide selector for intelligent section selection @style_guide_selector = [:style_guide_selector] || Aidp::StyleGuide::Selector.new(project_dir: project_dir) # FIX for issue #391: Initialize prompt evaluator for iteration threshold assessment @prompt_evaluator = [:prompt_evaluator] || PromptEvaluator.new(config) # Initialize security adapter for Rule of Two enforcement @security_adapter = [:security_adapter] || Aidp::Security::WorkLoopAdapter.new(project_dir: project_dir) end |
Instance Attribute Details
#checkpoint ⇒ Object (readonly)
Returns the value of attribute checkpoint.
46 47 48 |
# File 'lib/aidp/execute/work_loop_runner.rb', line 46 def checkpoint @checkpoint end |
#current_state ⇒ Object (readonly)
Returns the value of attribute current_state.
46 47 48 |
# File 'lib/aidp/execute/work_loop_runner.rb', line 46 def current_state @current_state end |
#guard_policy=(value) ⇒ Object (writeonly)
Sets the attribute guard_policy
47 48 49 |
# File 'lib/aidp/execute/work_loop_runner.rb', line 47 def guard_policy=(value) @guard_policy = value end |
#iteration_count ⇒ Object
Expose state for testability
45 46 47 |
# File 'lib/aidp/execute/work_loop_runner.rb', line 45 def iteration_count @iteration_count end |
#options ⇒ Object
Expose state for testability
45 46 47 |
# File 'lib/aidp/execute/work_loop_runner.rb', line 45 def @options end |
#persistent_tasklist ⇒ Object
Expose state for testability
45 46 47 |
# File 'lib/aidp/execute/work_loop_runner.rb', line 45 def persistent_tasklist @persistent_tasklist end |
#project_dir ⇒ Object (readonly)
Returns the value of attribute project_dir.
46 47 48 |
# File 'lib/aidp/execute/work_loop_runner.rb', line 46 def project_dir @project_dir end |
#prompt_manager ⇒ Object
Returns the value of attribute prompt_manager.
46 47 48 |
# File 'lib/aidp/execute/work_loop_runner.rb', line 46 def prompt_manager @prompt_manager end |
#state_history ⇒ Object (readonly)
Returns the value of attribute state_history.
46 47 48 |
# File 'lib/aidp/execute/work_loop_runner.rb', line 46 def state_history @state_history end |
#step_name ⇒ Object
Expose state for testability
45 46 47 |
# File 'lib/aidp/execute/work_loop_runner.rb', line 45 def step_name @step_name end |
#style_guide_selector=(value) ⇒ Object (writeonly)
Sets the attribute style_guide_selector
47 48 49 |
# File 'lib/aidp/execute/work_loop_runner.rb', line 47 def style_guide_selector=(value) @style_guide_selector = value end |
#test_runner ⇒ Object (readonly)
Returns the value of attribute test_runner.
46 47 48 |
# File 'lib/aidp/execute/work_loop_runner.rb', line 46 def test_runner @test_runner 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
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 |
# File 'lib/aidp/execute/work_loop_runner.rb', line 92 def execute_step(step_name, step_spec, context = {}) @step_name = step_name @work_context = context @iteration_count = 0 transition_to(:ready) Aidp.logger.info("work_loop", "Starting hybrid work loop execution", step: step_name, max_iterations: MAX_ITERATIONS) ("🔄 Starting hybrid work loop for step: #{step_name}", type: :info) (" 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 |