aidp 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +60 -214
  3. data/bin/aidp +1 -1
  4. data/lib/aidp/analysis/kb_inspector.rb +38 -23
  5. data/lib/aidp/analysis/seams.rb +2 -31
  6. data/lib/aidp/analysis/tree_sitter_grammar_loader.rb +1 -13
  7. data/lib/aidp/analysis/tree_sitter_scan.rb +3 -20
  8. data/lib/aidp/analyze/error_handler.rb +2 -75
  9. data/lib/aidp/analyze/json_file_storage.rb +292 -0
  10. data/lib/aidp/analyze/progress.rb +12 -0
  11. data/lib/aidp/analyze/progress_visualizer.rb +12 -17
  12. data/lib/aidp/analyze/ruby_maat_integration.rb +13 -31
  13. data/lib/aidp/analyze/runner.rb +256 -87
  14. data/lib/aidp/cli/jobs_command.rb +100 -432
  15. data/lib/aidp/cli.rb +309 -239
  16. data/lib/aidp/config.rb +298 -10
  17. data/lib/aidp/debug_logger.rb +195 -0
  18. data/lib/aidp/debug_mixin.rb +187 -0
  19. data/lib/aidp/execute/progress.rb +9 -0
  20. data/lib/aidp/execute/runner.rb +221 -40
  21. data/lib/aidp/execute/steps.rb +17 -7
  22. data/lib/aidp/execute/workflow_selector.rb +211 -0
  23. data/lib/aidp/harness/completion_checker.rb +268 -0
  24. data/lib/aidp/harness/condition_detector.rb +1526 -0
  25. data/lib/aidp/harness/config_loader.rb +373 -0
  26. data/lib/aidp/harness/config_manager.rb +382 -0
  27. data/lib/aidp/harness/config_schema.rb +1006 -0
  28. data/lib/aidp/harness/config_validator.rb +355 -0
  29. data/lib/aidp/harness/configuration.rb +477 -0
  30. data/lib/aidp/harness/enhanced_runner.rb +494 -0
  31. data/lib/aidp/harness/error_handler.rb +616 -0
  32. data/lib/aidp/harness/provider_config.rb +423 -0
  33. data/lib/aidp/harness/provider_factory.rb +306 -0
  34. data/lib/aidp/harness/provider_manager.rb +1269 -0
  35. data/lib/aidp/harness/provider_type_checker.rb +88 -0
  36. data/lib/aidp/harness/runner.rb +411 -0
  37. data/lib/aidp/harness/state/errors.rb +28 -0
  38. data/lib/aidp/harness/state/metrics.rb +219 -0
  39. data/lib/aidp/harness/state/persistence.rb +128 -0
  40. data/lib/aidp/harness/state/provider_state.rb +132 -0
  41. data/lib/aidp/harness/state/ui_state.rb +68 -0
  42. data/lib/aidp/harness/state/workflow_state.rb +123 -0
  43. data/lib/aidp/harness/state_manager.rb +586 -0
  44. data/lib/aidp/harness/status_display.rb +888 -0
  45. data/lib/aidp/harness/ui/base.rb +16 -0
  46. data/lib/aidp/harness/ui/enhanced_tui.rb +545 -0
  47. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +252 -0
  48. data/lib/aidp/harness/ui/error_handler.rb +132 -0
  49. data/lib/aidp/harness/ui/frame_manager.rb +361 -0
  50. data/lib/aidp/harness/ui/job_monitor.rb +500 -0
  51. data/lib/aidp/harness/ui/navigation/main_menu.rb +311 -0
  52. data/lib/aidp/harness/ui/navigation/menu_formatter.rb +120 -0
  53. data/lib/aidp/harness/ui/navigation/menu_item.rb +142 -0
  54. data/lib/aidp/harness/ui/navigation/menu_state.rb +139 -0
  55. data/lib/aidp/harness/ui/navigation/submenu.rb +202 -0
  56. data/lib/aidp/harness/ui/navigation/workflow_selector.rb +176 -0
  57. data/lib/aidp/harness/ui/progress_display.rb +280 -0
  58. data/lib/aidp/harness/ui/question_collector.rb +141 -0
  59. data/lib/aidp/harness/ui/spinner_group.rb +184 -0
  60. data/lib/aidp/harness/ui/spinner_helper.rb +152 -0
  61. data/lib/aidp/harness/ui/status_manager.rb +312 -0
  62. data/lib/aidp/harness/ui/status_widget.rb +280 -0
  63. data/lib/aidp/harness/ui/workflow_controller.rb +312 -0
  64. data/lib/aidp/harness/user_interface.rb +2381 -0
  65. data/lib/aidp/provider_manager.rb +131 -7
  66. data/lib/aidp/providers/anthropic.rb +28 -103
  67. data/lib/aidp/providers/base.rb +170 -0
  68. data/lib/aidp/providers/cursor.rb +52 -181
  69. data/lib/aidp/providers/gemini.rb +24 -107
  70. data/lib/aidp/providers/macos_ui.rb +99 -5
  71. data/lib/aidp/providers/opencode.rb +194 -0
  72. data/lib/aidp/storage/csv_storage.rb +172 -0
  73. data/lib/aidp/storage/file_manager.rb +214 -0
  74. data/lib/aidp/storage/json_storage.rb +140 -0
  75. data/lib/aidp/version.rb +1 -1
  76. data/lib/aidp.rb +54 -39
  77. data/templates/COMMON/AGENT_BASE.md +11 -0
  78. data/templates/EXECUTE/00_PRD.md +4 -4
  79. data/templates/EXECUTE/02_ARCHITECTURE.md +5 -4
  80. data/templates/EXECUTE/07_TEST_PLAN.md +4 -1
  81. data/templates/EXECUTE/08_TASKS.md +4 -4
  82. data/templates/EXECUTE/10_IMPLEMENTATION_AGENT.md +4 -4
  83. data/templates/README.md +279 -0
  84. data/templates/aidp-development.yml.example +373 -0
  85. data/templates/aidp-minimal.yml.example +48 -0
  86. data/templates/aidp-production.yml.example +475 -0
  87. data/templates/aidp.yml.example +598 -0
  88. metadata +93 -69
  89. data/lib/aidp/analyze/agent_personas.rb +0 -71
  90. data/lib/aidp/analyze/agent_tool_executor.rb +0 -439
  91. data/lib/aidp/analyze/data_retention_manager.rb +0 -421
  92. data/lib/aidp/analyze/database.rb +0 -260
  93. data/lib/aidp/analyze/dependencies.rb +0 -335
  94. data/lib/aidp/analyze/export_manager.rb +0 -418
  95. data/lib/aidp/analyze/focus_guidance.rb +0 -517
  96. data/lib/aidp/analyze/incremental_analyzer.rb +0 -533
  97. data/lib/aidp/analyze/language_analysis_strategies.rb +0 -897
  98. data/lib/aidp/analyze/large_analysis_progress.rb +0 -499
  99. data/lib/aidp/analyze/memory_manager.rb +0 -339
  100. data/lib/aidp/analyze/metrics_storage.rb +0 -336
  101. data/lib/aidp/analyze/parallel_processor.rb +0 -454
  102. data/lib/aidp/analyze/performance_optimizer.rb +0 -691
  103. data/lib/aidp/analyze/repository_chunker.rb +0 -697
  104. data/lib/aidp/analyze/static_analysis_detector.rb +0 -577
  105. data/lib/aidp/analyze/storage.rb +0 -655
  106. data/lib/aidp/analyze/tool_configuration.rb +0 -441
  107. data/lib/aidp/analyze/tool_modernization.rb +0 -750
  108. data/lib/aidp/database/pg_adapter.rb +0 -148
  109. data/lib/aidp/database_config.rb +0 -69
  110. data/lib/aidp/database_connection.rb +0 -72
  111. data/lib/aidp/job_manager.rb +0 -41
  112. data/lib/aidp/jobs/base_job.rb +0 -45
  113. data/lib/aidp/jobs/provider_execution_job.rb +0 -83
  114. data/lib/aidp/project_detector.rb +0 -117
  115. data/lib/aidp/providers/agent_supervisor.rb +0 -348
  116. data/lib/aidp/providers/supervised_base.rb +0 -317
  117. data/lib/aidp/providers/supervised_cursor.rb +0 -22
  118. data/lib/aidp/sync.rb +0 -13
  119. data/lib/aidp/workspace.rb +0 -19
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aidp
4
+ module Harness
5
+ # Shared module for provider type checking functionality
6
+ # This eliminates duplication across ProviderConfig and ConfigManager
7
+ module ProviderTypeChecker
8
+ # Check if provider is usage-based (pay per token)
9
+ def usage_based_provider?(provider_name_or_options = {}, options = {})
10
+ # Handle both ConfigManager (provider_name, options) and ProviderConfig (options) signatures
11
+ if provider_name_or_options.is_a?(String) || provider_name_or_options.is_a?(Symbol)
12
+ provider_name = provider_name_or_options
13
+ get_provider_type(provider_name, options) == "usage_based"
14
+ else
15
+ # ProviderConfig signature: usage_based_provider?(options)
16
+ options = provider_name_or_options
17
+ get_type(options) == "usage_based"
18
+ end
19
+ end
20
+
21
+ # Check if provider is subscription-based (unlimited within limits)
22
+ def subscription_provider?(provider_name_or_options = {}, options = {})
23
+ if provider_name_or_options.is_a?(String) || provider_name_or_options.is_a?(Symbol)
24
+ provider_name = provider_name_or_options
25
+ get_provider_type(provider_name, options) == "subscription"
26
+ else
27
+ options = provider_name_or_options
28
+ get_type(options) == "subscription"
29
+ end
30
+ end
31
+
32
+ # Check if provider is passthrough (inherits billing from underlying service)
33
+ def passthrough_provider?(provider_name_or_options = {}, options = {})
34
+ if provider_name_or_options.is_a?(String) || provider_name_or_options.is_a?(Symbol)
35
+ provider_name = provider_name_or_options
36
+ get_provider_type(provider_name, options) == "passthrough"
37
+ else
38
+ options = provider_name_or_options
39
+ get_type(options) == "passthrough"
40
+ end
41
+ end
42
+
43
+ # Get provider type with fallback to subscription (ConfigManager signature)
44
+ def get_provider_type(provider_name, options = {})
45
+ provider_config = get_provider_config(provider_name, options)
46
+ return "subscription" unless provider_config
47
+
48
+ provider_config[:type] || provider_config["type"] || "subscription"
49
+ end
50
+
51
+ # Check if provider requires API key
52
+ def requires_api_key?(provider_name_or_options = {}, options = {})
53
+ usage_based_provider?(provider_name_or_options, options)
54
+ end
55
+
56
+ # Check if provider has underlying service (passthrough)
57
+ def has_underlying_service?(provider_name_or_options = {}, options = {})
58
+ return false unless passthrough_provider?(provider_name_or_options, options)
59
+
60
+ if provider_name_or_options.is_a?(String) || provider_name_or_options.is_a?(Symbol)
61
+ provider_name = provider_name_or_options
62
+ provider_config = get_provider_config(provider_name, options)
63
+ else
64
+ options = provider_name_or_options
65
+ provider_config = get_config(options)
66
+ end
67
+
68
+ underlying_service = provider_config[:underlying_service] || provider_config["underlying_service"]
69
+ !underlying_service.nil? && !underlying_service.empty?
70
+ end
71
+
72
+ # Get underlying service name for passthrough providers
73
+ def get_underlying_service(provider_name_or_options = {}, options = {})
74
+ return nil unless passthrough_provider?(provider_name_or_options, options)
75
+
76
+ if provider_name_or_options.is_a?(String) || provider_name_or_options.is_a?(Symbol)
77
+ provider_name = provider_name_or_options
78
+ provider_config = get_provider_config(provider_name, options)
79
+ else
80
+ options = provider_name_or_options
81
+ provider_config = get_config(options)
82
+ end
83
+
84
+ provider_config[:underlying_service] || provider_config["underlying_service"]
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,411 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "timeout"
4
+ require "json"
5
+ require_relative "configuration"
6
+ require_relative "state_manager"
7
+ require_relative "condition_detector"
8
+ require_relative "provider_manager"
9
+ require_relative "user_interface"
10
+ require_relative "error_handler"
11
+ require_relative "status_display"
12
+ require_relative "completion_checker"
13
+
14
+ module Aidp
15
+ module Harness
16
+ # Main harness runner that orchestrates the execution loop
17
+ class Runner
18
+ # Harness execution states
19
+ STATES = {
20
+ idle: "idle",
21
+ running: "running",
22
+ paused: "paused",
23
+ waiting_for_user: "waiting_for_user",
24
+ waiting_for_rate_limit: "waiting_for_rate_limit",
25
+ stopped: "stopped",
26
+ completed: "completed",
27
+ error: "error"
28
+ }.freeze
29
+
30
+ def initialize(project_dir, mode = :analyze, options = {})
31
+ @project_dir = project_dir
32
+ @mode = mode.to_sym
33
+ @options = options
34
+ @state = STATES[:idle]
35
+ @start_time = nil
36
+ @current_step = nil
37
+ @current_provider = nil
38
+ @user_input = options[:user_input] || {} # Include user input from workflow selection
39
+ @execution_log = []
40
+
41
+ # Store workflow configuration
42
+ @selected_steps = options[:selected_steps]
43
+ @workflow_type = options[:workflow_type]
44
+
45
+ # Initialize components
46
+ @configuration = Configuration.new(project_dir)
47
+ @state_manager = StateManager.new(project_dir, @mode)
48
+ @condition_detector = ConditionDetector.new
49
+ @provider_manager = ProviderManager.new(@configuration)
50
+ @user_interface = UserInterface.new
51
+ @error_handler = ErrorHandler.new(@provider_manager, @configuration)
52
+ @status_display = StatusDisplay.new
53
+ @completion_checker = CompletionChecker.new(@project_dir, @workflow_type)
54
+ end
55
+
56
+ # Main execution method - runs the harness loop
57
+ def run
58
+ @state = STATES[:running]
59
+ @start_time = Time.now
60
+
61
+ log_execution("Harness started", {mode: @mode, project_dir: @project_dir})
62
+
63
+ begin
64
+ # Load existing state if resuming
65
+ load_state if @state_manager.has_state?
66
+
67
+ # Get the appropriate runner for the mode
68
+ runner = get_mode_runner
69
+
70
+ # Main execution loop
71
+ loop do
72
+ break if should_stop?
73
+
74
+ # Check for pause conditions
75
+ if should_pause?
76
+ handle_pause_condition
77
+ next
78
+ end
79
+
80
+ # Get next step to execute
81
+ next_step = get_next_step(runner)
82
+ break unless next_step
83
+
84
+ # Execute the step
85
+ execute_step(runner, next_step)
86
+
87
+ # Update state
88
+ update_state
89
+ end
90
+
91
+ # Mark as completed if we finished all steps AND all completion criteria are met
92
+ if all_steps_completed?(runner)
93
+ completion_status = @completion_checker.completion_status
94
+ if completion_status[:all_complete]
95
+ @state = STATES[:completed]
96
+ log_execution("Harness completed successfully - all criteria met", completion_status)
97
+ else
98
+ log_execution("Steps completed but completion criteria not met", completion_status)
99
+ puts "\n⚠️ All steps completed but some completion criteria not met:"
100
+ puts completion_status[:summary]
101
+
102
+ # Ask user if they want to continue anyway
103
+ if @user_interface.get_confirmation("Continue anyway? This may indicate issues that should be addressed.", default: false)
104
+ @state = STATES[:completed]
105
+ log_execution("Harness completed with user override")
106
+ else
107
+ @state = STATES[:error]
108
+ log_execution("Harness stopped due to unmet completion criteria")
109
+ end
110
+ end
111
+ end
112
+ rescue => e
113
+ @state = STATES[:error]
114
+ log_execution("Harness error: #{e.message}", {error: e.class.name})
115
+ handle_error(e)
116
+ ensure
117
+ # Save state before exiting
118
+ save_state
119
+ cleanup
120
+ end
121
+
122
+ {status: @state, message: get_completion_message}
123
+ end
124
+
125
+ # Pause the harness execution
126
+ def pause
127
+ return unless @state == STATES[:running]
128
+
129
+ @state = STATES[:paused]
130
+ log_execution("Harness paused by user")
131
+ @status_display.show_paused_status
132
+ end
133
+
134
+ # Resume the harness execution
135
+ def resume
136
+ return unless @state == STATES[:paused]
137
+
138
+ @state = STATES[:running]
139
+ log_execution("Harness resumed by user")
140
+ @status_display.show_resumed_status
141
+ end
142
+
143
+ # Stop the harness execution
144
+ def stop
145
+ @state = STATES[:stopped]
146
+ log_execution("Harness stopped by user")
147
+ @status_display.show_stopped_status
148
+ end
149
+
150
+ # Get current harness status
151
+ def status
152
+ {
153
+ state: @state,
154
+ mode: @mode,
155
+ current_step: @current_step,
156
+ current_provider: @current_provider,
157
+ start_time: @start_time,
158
+ duration: @start_time ? Time.now - @start_time : 0,
159
+ user_input_count: @user_input.size,
160
+ execution_log_count: @execution_log.size,
161
+ progress: @state_manager.progress_summary
162
+ }
163
+ end
164
+
165
+ # Get detailed status including all components
166
+ def detailed_status
167
+ {
168
+ harness: status,
169
+ configuration: {
170
+ default_provider: @configuration.default_provider,
171
+ fallback_providers: @configuration.fallback_providers,
172
+ max_retries: @configuration.max_retries
173
+ },
174
+ provider_manager: @provider_manager.status,
175
+ error_stats: @error_handler.error_stats
176
+ }
177
+ end
178
+
179
+ private
180
+
181
+ def get_mode_runner
182
+ case @mode
183
+ when :analyze
184
+ Aidp::Analyze::Runner.new(@project_dir, self)
185
+ when :execute
186
+ Aidp::Execute::Runner.new(@project_dir, self)
187
+ else
188
+ raise ArgumentError, "Unsupported mode: #{@mode}"
189
+ end
190
+ end
191
+
192
+ def get_next_step(runner)
193
+ # Use the mode runner's next_step method
194
+ runner.next_step
195
+ end
196
+
197
+ def execute_step(runner, step_name)
198
+ @current_step = step_name
199
+ log_execution("Executing step: #{step_name}")
200
+
201
+ # Mark step as in progress using the runner's method
202
+ runner.mark_step_in_progress(step_name)
203
+
204
+ # Update status display
205
+ @status_display.update_current_step(step_name)
206
+
207
+ # Get current provider
208
+ @current_provider = @provider_manager.current_provider
209
+ @status_display.update_current_provider(@current_provider)
210
+
211
+ # Execute the step with error handling
212
+ result = @error_handler.execute_with_retry do
213
+ # Merge harness options with user input
214
+ step_options = @options.merge(user_input: @user_input)
215
+ runner.run_step(step_name, step_options)
216
+ end
217
+
218
+ # Check for conditions that require user interaction
219
+ if @condition_detector.needs_user_feedback?(result)
220
+ handle_user_feedback_request(result)
221
+ end
222
+
223
+ # Check for rate limiting
224
+ if @condition_detector.is_rate_limited?(result)
225
+ handle_rate_limit(result)
226
+ end
227
+
228
+ # Mark step as completed if successful using the runner's method
229
+ if result && result[:status] == "completed"
230
+ runner.mark_step_completed(step_name)
231
+ end
232
+
233
+ log_execution("Step completed: #{step_name}", {result: result})
234
+ result
235
+ end
236
+
237
+ def handle_user_feedback_request(result)
238
+ @state = STATES[:waiting_for_user]
239
+ log_execution("Waiting for user feedback")
240
+
241
+ # Extract questions from result
242
+ questions = @condition_detector.extract_questions(result)
243
+
244
+ # Collect user input
245
+ user_responses = @user_interface.collect_feedback(questions)
246
+
247
+ # Store user input in both local state and state manager
248
+ @user_input.merge!(user_responses)
249
+ user_responses.each do |key, value|
250
+ @state_manager.add_user_input(key, value)
251
+ end
252
+
253
+ @state = STATES[:running]
254
+ log_execution("User feedback collected", {responses: user_responses.keys})
255
+ end
256
+
257
+ def handle_rate_limit(_result)
258
+ @state = STATES[:waiting_for_rate_limit]
259
+ log_execution("Rate limit detected, switching provider")
260
+
261
+ # Mark current provider as rate limited
262
+ @provider_manager.mark_rate_limited(@current_provider)
263
+
264
+ # Switch to next provider
265
+ next_provider = @provider_manager.switch_provider
266
+ @current_provider = next_provider
267
+
268
+ if next_provider
269
+ @state = STATES[:running]
270
+ log_execution("Switched to provider: #{next_provider}")
271
+ else
272
+ # All providers rate limited, wait for reset
273
+ wait_for_rate_limit_reset
274
+ end
275
+ end
276
+
277
+ def wait_for_rate_limit_reset
278
+ reset_time = @provider_manager.next_reset_time
279
+ if reset_time
280
+ @status_display.show_rate_limit_wait(reset_time)
281
+ sleep_until_reset(reset_time)
282
+ @state = STATES[:running]
283
+ else
284
+ @state = STATES[:error]
285
+ raise "All providers rate limited with no reset time available"
286
+ end
287
+ end
288
+
289
+ def sleep_until_reset(reset_time)
290
+ while Time.now < reset_time && @state == STATES[:waiting_for_rate_limit]
291
+ remaining = reset_time - Time.now
292
+ @status_display.update_rate_limit_countdown(remaining)
293
+ if ENV["RACK_ENV"] == "test" || defined?(RSpec)
294
+ sleep(1)
295
+ else
296
+ Async::Task.current.sleep(1)
297
+ end
298
+ end
299
+ end
300
+
301
+ def should_stop?
302
+ @state == STATES[:stopped] ||
303
+ @state == STATES[:completed] ||
304
+ @state == STATES[:error]
305
+ end
306
+
307
+ def should_pause?
308
+ @state == STATES[:paused] ||
309
+ @state == STATES[:waiting_for_user] ||
310
+ @state == STATES[:waiting_for_rate_limit]
311
+ end
312
+
313
+ def handle_pause_condition
314
+ case @state
315
+ when STATES[:paused]
316
+ # Wait for user to resume
317
+ if ENV["RACK_ENV"] == "test" || defined?(RSpec)
318
+ sleep(1)
319
+ else
320
+ Async::Task.current.sleep(1)
321
+ end
322
+ when STATES[:waiting_for_user]
323
+ # User interface handles this
324
+ nil
325
+ when STATES[:waiting_for_rate_limit]
326
+ # Rate limit handling
327
+ nil
328
+ end
329
+ end
330
+
331
+ def all_steps_completed?(runner)
332
+ # Use the mode runner's all_steps_completed? method
333
+ runner.all_steps_completed?
334
+ end
335
+
336
+ def update_state
337
+ @state_manager.update_state({
338
+ state: @state,
339
+ current_step: @current_step,
340
+ current_provider: @current_provider,
341
+ user_input: @user_input,
342
+ last_updated: Time.now
343
+ })
344
+ end
345
+
346
+ def load_state
347
+ if @state_manager.has_state?
348
+ state_data = @state_manager.load_state
349
+ @current_step = state_data[:current_step]
350
+ @current_provider = state_data[:current_provider]
351
+ @user_input = @state_manager.user_input
352
+ log_execution("Loaded existing state", state_data)
353
+ else
354
+ log_execution("No existing state found, starting fresh")
355
+ end
356
+ end
357
+
358
+ def save_state
359
+ # Save harness-specific state
360
+ @state_manager.save_state({
361
+ state: @state,
362
+ current_step: @current_step,
363
+ current_provider: @current_provider,
364
+ user_input: @user_input,
365
+ execution_log: @execution_log,
366
+ last_saved: Time.now
367
+ })
368
+
369
+ # Also save execution log entries to state manager
370
+ @execution_log.each do |entry|
371
+ @state_manager.add_execution_log(entry)
372
+ end
373
+ end
374
+
375
+ def handle_error(error)
376
+ @error_handler.handle_error(error, self)
377
+ end
378
+
379
+ def cleanup
380
+ @status_display.cleanup
381
+ log_execution("Harness cleanup completed")
382
+ end
383
+
384
+ def log_execution(message, data = {})
385
+ log_entry = {
386
+ timestamp: Time.now,
387
+ message: message,
388
+ state: @state,
389
+ data: data
390
+ }
391
+ @execution_log << log_entry
392
+
393
+ # Also log to standard logging if available
394
+ puts "[#{Time.now.strftime("%H:%M:%S")}] #{message}" if ENV["AIDP_DEBUG"] == "1"
395
+ end
396
+
397
+ def get_completion_message
398
+ case @state
399
+ when STATES[:completed]
400
+ "Harness completed successfully. All steps finished."
401
+ when STATES[:stopped]
402
+ "Harness stopped by user."
403
+ when STATES[:error]
404
+ "Harness encountered an error and stopped."
405
+ else
406
+ "Harness finished in state: #{@state}"
407
+ end
408
+ end
409
+ end
410
+ end
411
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aidp
4
+ module Harness
5
+ module State
6
+ # Base error class for state management
7
+ class StateError < StandardError; end
8
+
9
+ # Raised when state file operations fail
10
+ class PersistenceError < StateError; end
11
+
12
+ # Raised when state lock cannot be acquired
13
+ class LockTimeoutError < StateError; end
14
+
15
+ # Raised when state data is invalid or corrupted
16
+ class InvalidStateError < StateError; end
17
+
18
+ # Raised when provider state operations fail
19
+ class ProviderStateError < StateError; end
20
+
21
+ # Raised when workflow state operations fail
22
+ class WorkflowStateError < StateError; end
23
+
24
+ # Raised when metrics calculations fail
25
+ class MetricsError < StateError; end
26
+ end
27
+ end
28
+ end