code_healer 0.1.0
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +70 -0
- data/GEM_SUMMARY.md +307 -0
- data/README.md +281 -0
- data/code_healer.gemspec +77 -0
- data/config/code_healer.yml.example +104 -0
- data/docs/INSTALLATION.md +439 -0
- data/examples/basic_usage.rb +160 -0
- data/exe/code_healer-setup +7 -0
- data/lib/code_healer/application_job.rb +7 -0
- data/lib/code_healer/business_context_analyzer.rb +464 -0
- data/lib/code_healer/business_context_loader.rb +273 -0
- data/lib/code_healer/business_context_manager.rb +297 -0
- data/lib/code_healer/business_logic_generator.rb +94 -0
- data/lib/code_healer/business_rule_applier.rb +54 -0
- data/lib/code_healer/claude_code_evolution_handler.rb +224 -0
- data/lib/code_healer/claude_error_monitor.rb +48 -0
- data/lib/code_healer/config_manager.rb +275 -0
- data/lib/code_healer/context_aware_prompt_builder.rb +153 -0
- data/lib/code_healer/core.rb +513 -0
- data/lib/code_healer/error_handler.rb +141 -0
- data/lib/code_healer/evolution_job.rb +99 -0
- data/lib/code_healer/global_handler.rb +130 -0
- data/lib/code_healer/healing_job.rb +167 -0
- data/lib/code_healer/mcp.rb +108 -0
- data/lib/code_healer/mcp_prompts.rb +111 -0
- data/lib/code_healer/mcp_server.rb +389 -0
- data/lib/code_healer/mcp_tools.rb +2364 -0
- data/lib/code_healer/pull_request_creator.rb +143 -0
- data/lib/code_healer/setup.rb +390 -0
- data/lib/code_healer/simple_evolution.rb +737 -0
- data/lib/code_healer/simple_global_handler.rb +122 -0
- data/lib/code_healer/simple_healer.rb +515 -0
- data/lib/code_healer/terminal_integration.rb +87 -0
- data/lib/code_healer/usage_analyzer.rb +92 -0
- data/lib/code_healer/version.rb +5 -0
- data/lib/code_healer.rb +67 -0
- metadata +411 -0
@@ -0,0 +1,160 @@
|
|
1
|
+
# Basic Usage Example
|
2
|
+
# This example shows how to use CodeHealer in a simple Rails application
|
3
|
+
|
4
|
+
# 1. Add to Gemfile
|
5
|
+
puts "Add to your Gemfile:"
|
6
|
+
puts "gem 'code_healer'"
|
7
|
+
puts
|
8
|
+
|
9
|
+
# 2. Configuration
|
10
|
+
puts "Create config/code_healer.yml:"
|
11
|
+
puts <<~YAML
|
12
|
+
---
|
13
|
+
enabled: true
|
14
|
+
|
15
|
+
allowed_classes:
|
16
|
+
- User
|
17
|
+
- Order
|
18
|
+
- PaymentProcessor
|
19
|
+
|
20
|
+
evolution_strategy:
|
21
|
+
method: "api"
|
22
|
+
|
23
|
+
business_context:
|
24
|
+
enabled: true
|
25
|
+
User:
|
26
|
+
domain: "User Management"
|
27
|
+
key_rules:
|
28
|
+
- "Email must be unique and valid"
|
29
|
+
- "Password must meet security requirements"
|
30
|
+
YAML
|
31
|
+
puts
|
32
|
+
|
33
|
+
# 3. Model Example
|
34
|
+
puts "Example model (app/models/user.rb):"
|
35
|
+
puts <<~RUBY
|
36
|
+
class User < ApplicationRecord
|
37
|
+
def calculate_discount(amount, percentage)
|
38
|
+
# This will trigger evolution if an error occurs
|
39
|
+
amount * (percentage / 100.0)
|
40
|
+
end
|
41
|
+
|
42
|
+
def validate_payment(card_number, expiry_date)
|
43
|
+
# Another method that can evolve
|
44
|
+
card_number.length == 16 && expiry_date > Date.current
|
45
|
+
end
|
46
|
+
end
|
47
|
+
RUBY
|
48
|
+
puts
|
49
|
+
|
50
|
+
# 4. Controller Example
|
51
|
+
puts "Example controller (app/controllers/api/users_controller.rb):"
|
52
|
+
puts <<~RUBY
|
53
|
+
class Api::UsersController < ApplicationController
|
54
|
+
def calculate_discount
|
55
|
+
user = User.find(params[:id])
|
56
|
+
discount = user.calculate_discount(
|
57
|
+
params[:amount].to_f,
|
58
|
+
params[:percentage].to_f
|
59
|
+
)
|
60
|
+
|
61
|
+
render json: { discount: discount }
|
62
|
+
rescue => e
|
63
|
+
# CodeHealer will catch this error and fix it
|
64
|
+
render json: { error: e.message }, status: 500
|
65
|
+
end
|
66
|
+
end
|
67
|
+
RUBY
|
68
|
+
puts
|
69
|
+
|
70
|
+
# 5. Routes
|
71
|
+
puts "Add to config/routes.rb:"
|
72
|
+
puts <<~RUBY
|
73
|
+
Rails.application.routes.draw do
|
74
|
+
namespace :api do
|
75
|
+
resources :users do
|
76
|
+
member do
|
77
|
+
post :calculate_discount
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
RUBY
|
83
|
+
puts
|
84
|
+
|
85
|
+
# 6. Test the Evolution
|
86
|
+
puts "Test the evolution:"
|
87
|
+
puts <<~BASH
|
88
|
+
# Start your Rails server
|
89
|
+
rails server
|
90
|
+
|
91
|
+
# In another terminal, trigger an error
|
92
|
+
curl -X POST http://localhost:3000/api/users/1/calculate_discount \\
|
93
|
+
-H "Content-Type: application/json" \\
|
94
|
+
-d '{"amount": 100, "percentage": nil}'
|
95
|
+
BASH
|
96
|
+
puts
|
97
|
+
|
98
|
+
# 7. Watch the Evolution
|
99
|
+
puts "Watch the healing in action:"
|
100
|
+
puts <<~BASH
|
101
|
+
# Check Rails logs
|
102
|
+
tail -f log/development.log
|
103
|
+
|
104
|
+
# Check Sidekiq (if using background jobs)
|
105
|
+
tail -f log/sidekiq.log
|
106
|
+
|
107
|
+
# Monitor Sidekiq Web UI
|
108
|
+
open http://localhost:3000/sidekiq
|
109
|
+
BASH
|
110
|
+
puts
|
111
|
+
|
112
|
+
# 8. Business Requirements
|
113
|
+
puts "Create business_requirements/user_management.md:"
|
114
|
+
puts <<~MARKDOWN
|
115
|
+
# User Management Business Rules
|
116
|
+
|
117
|
+
## Authentication
|
118
|
+
- Users must have valid email addresses
|
119
|
+
- Passwords must be at least 8 characters
|
120
|
+
- Failed login attempts should be logged
|
121
|
+
|
122
|
+
## Discount Calculation
|
123
|
+
- Discount cannot exceed 50% of total amount
|
124
|
+
- Negative percentages are not allowed
|
125
|
+
- Zero amounts should return zero discount
|
126
|
+
|
127
|
+
## Data Validation
|
128
|
+
- All user input must be sanitized
|
129
|
+
- Email uniqueness is enforced at database level
|
130
|
+
MARKDOWN
|
131
|
+
puts
|
132
|
+
|
133
|
+
# 9. Environment Variables
|
134
|
+
puts "Set environment variables (.env):"
|
135
|
+
puts <<~ENV
|
136
|
+
OPENAI_API_KEY=your_openai_api_key_here
|
137
|
+
GITHUB_TOKEN=your_github_token_here
|
138
|
+
ENV
|
139
|
+
puts
|
140
|
+
|
141
|
+
# 10. Sidekiq Configuration
|
142
|
+
puts "Create config/sidekiq.yml:"
|
143
|
+
puts <<~YAML
|
144
|
+
:concurrency: 5
|
145
|
+
:queues:
|
146
|
+
- [evolution, 2]
|
147
|
+
- [default, 1]
|
148
|
+
YAML
|
149
|
+
puts
|
150
|
+
|
151
|
+
puts "🎉 Your CodeHealer application is ready!"
|
152
|
+
puts
|
153
|
+
puts "Next steps:"
|
154
|
+
puts "1. Run 'bundle install'"
|
155
|
+
puts "2. Start Redis: 'redis-server'"
|
156
|
+
puts "3. Start Sidekiq: 'bundle exec sidekiq'"
|
157
|
+
puts "4. Start Rails: 'rails server'"
|
158
|
+
puts "5. Test with the curl command above"
|
159
|
+
puts
|
160
|
+
puts "Watch the magic happen! 🚀"
|
@@ -0,0 +1,7 @@
|
|
1
|
+
class ApplicationJob < ActiveJob::Base
|
2
|
+
# Automatically retry jobs that encountered a deadlock
|
3
|
+
# retry_on ActiveRecord::Deadlocked
|
4
|
+
|
5
|
+
# Most jobs are safe to ignore if the underlying records are no longer available
|
6
|
+
# discard_on ActiveJob::DeserializationError
|
7
|
+
end
|
@@ -0,0 +1,464 @@
|
|
1
|
+
module CodeHealer
|
2
|
+
# Business Context Analyzer
|
3
|
+
# Reads business context from files and applies business rules before evolution
|
4
|
+
# Works like a developer analyzing business requirements before fixing bugs
|
5
|
+
class BusinessContextAnalyzer
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def analyze_error_for_business_context(error, class_name, method_name, file_path)
|
9
|
+
puts "🔍 DEBUG: analyze_error_for_business_context called with:"
|
10
|
+
puts " - error: #{error.class} (#{error.message})"
|
11
|
+
puts " - class_name: #{class_name.inspect}"
|
12
|
+
puts " - method_name: #{method_name.inspect}"
|
13
|
+
puts " - file_path: #{file_path.inspect}"
|
14
|
+
|
15
|
+
puts "🔍 Analyzing business context for #{class_name}##{method_name} error..."
|
16
|
+
|
17
|
+
# Load business context from various sources
|
18
|
+
puts " 🔍 DEBUG: About to call load_business_context..."
|
19
|
+
business_context = load_business_context(file_path, class_name, method_name)
|
20
|
+
puts " 🔍 DEBUG: load_business_context returned: #{business_context.keys}"
|
21
|
+
|
22
|
+
# Analyze the error against business rules
|
23
|
+
puts " 🔍 DEBUG: About to call analyze_error_against_business_rules..."
|
24
|
+
analysis_result = analyze_error_against_business_rules(error, business_context, class_name, method_name)
|
25
|
+
puts " 🔍 DEBUG: analyze_error_against_business_rules returned: #{analysis_result.keys}"
|
26
|
+
|
27
|
+
# Determine evolution strategy based on business context
|
28
|
+
puts " 🔍 DEBUG: About to call determine_evolution_strategy..."
|
29
|
+
evolution_strategy = determine_evolution_strategy(analysis_result, error, class_name, method_name)
|
30
|
+
puts " 🔍 DEBUG: determine_evolution_strategy returned: #{evolution_strategy.keys}"
|
31
|
+
|
32
|
+
puts " 📋 Business context analysis complete"
|
33
|
+
puts " 🎯 Evolution strategy: #{evolution_strategy[:strategy]}"
|
34
|
+
puts " 💼 Business impact: #{evolution_strategy[:business_impact]}"
|
35
|
+
|
36
|
+
{
|
37
|
+
business_context: business_context,
|
38
|
+
analysis: analysis_result,
|
39
|
+
evolution_strategy: evolution_strategy
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def load_business_context(file_path, class_name, method_name)
|
46
|
+
puts " 🔍 DEBUG: load_business_context called with:"
|
47
|
+
puts " - file_path: #{file_path.inspect}"
|
48
|
+
puts " - class_name: #{class_name.inspect}"
|
49
|
+
puts " - method_name: #{method_name.inspect}"
|
50
|
+
|
51
|
+
puts " 🔍 DEBUG: About to call load_requirements_documents..."
|
52
|
+
requirements = load_requirements_documents(file_path, class_name, method_name)
|
53
|
+
puts " 🔍 DEBUG: load_requirements_documents returned #{requirements.length} documents"
|
54
|
+
|
55
|
+
puts " 🔍 DEBUG: About to call load_business_rules_documents..."
|
56
|
+
business_rules = load_business_rules_documents(file_path, class_name, method_name)
|
57
|
+
puts " 🔍 DEBUG: load_business_rules_documents returned #{business_rules.length} documents"
|
58
|
+
|
59
|
+
puts " 🔍 DEBUG: About to call load_error_handling_documents..."
|
60
|
+
error_handling = load_error_handling_documents(file_path, class_name, method_name)
|
61
|
+
puts " 🔍 DEBUG: load_error_handling_documents returned #{error_handling.length} documents"
|
62
|
+
|
63
|
+
puts " 🔍 DEBUG: About to call load_user_experience_documents..."
|
64
|
+
user_experience = load_user_experience_documents(file_path, class_name, method_name)
|
65
|
+
puts " 🔍 DEBUG: load_user_experience_documents returned #{user_experience.length} documents"
|
66
|
+
|
67
|
+
puts " 🔍 DEBUG: About to call load_domain_specific_context..."
|
68
|
+
domain_specific = load_domain_specific_context(class_name, method_name)
|
69
|
+
puts " 🔍 DEBUG: load_domain_specific_context returned: #{domain_specific.inspect}"
|
70
|
+
|
71
|
+
context = {
|
72
|
+
requirements: requirements,
|
73
|
+
business_rules: business_rules,
|
74
|
+
error_handling: error_handling,
|
75
|
+
user_experience: user_experience,
|
76
|
+
domain_specific: domain_specific
|
77
|
+
}
|
78
|
+
|
79
|
+
puts " 🔍 DEBUG: Final context keys: #{context.keys}"
|
80
|
+
puts " 🔍 DEBUG: Total sources: #{context.values.flatten.length}"
|
81
|
+
puts " 📚 Loaded business context from #{context.values.flatten.length} sources"
|
82
|
+
context
|
83
|
+
end
|
84
|
+
|
85
|
+
def load_requirements_documents(file_path, class_name = nil, method_name = nil)
|
86
|
+
documents = []
|
87
|
+
puts " 🔍 DEBUG: load_requirements_documents called with file_path: #{file_path.inspect}, class_name: #{class_name.inspect}, method_name: #{method_name.inspect}"
|
88
|
+
|
89
|
+
# Look for requirements documents in various locations
|
90
|
+
search_paths = [
|
91
|
+
File.dirname(file_path),
|
92
|
+
Rails.root.join('business_requirements'),
|
93
|
+
Rails.root.join('docs'),
|
94
|
+
Rails.root.join('requirements'),
|
95
|
+
Rails.root.join('PRD'),
|
96
|
+
Rails.root.join('specs')
|
97
|
+
]
|
98
|
+
|
99
|
+
search_paths.each do |search_path|
|
100
|
+
next unless Dir.exist?(search_path)
|
101
|
+
|
102
|
+
Dir.glob(File.join(search_path, '**/*.{md,txt,yml,yaml}')).each do |doc_path|
|
103
|
+
next unless File.readable?(doc_path)
|
104
|
+
|
105
|
+
content = File.read(doc_path)
|
106
|
+
if content.match?(/requirement|must|should|shall/i)
|
107
|
+
relevance = class_name && method_name ? calculate_relevance(content, class_name, method_name) : 0.5
|
108
|
+
documents << {
|
109
|
+
path: doc_path,
|
110
|
+
type: 'requirement',
|
111
|
+
content: content,
|
112
|
+
relevance: relevance
|
113
|
+
}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
documents.sort_by { |doc| -doc[:relevance] }
|
119
|
+
end
|
120
|
+
|
121
|
+
def load_business_rules_documents(file_path, class_name = nil, method_name = nil)
|
122
|
+
documents = []
|
123
|
+
puts " 🔍 DEBUG: load_business_rules_documents called with file_path: #{file_path.inspect}, class_name: #{class_name.inspect}, method_name: #{method_name.inspect}"
|
124
|
+
|
125
|
+
# Look for business rules documents
|
126
|
+
search_paths = [
|
127
|
+
File.dirname(file_path),
|
128
|
+
Rails.root.join('business_requirements'),
|
129
|
+
Rails.root.join('business_rules'),
|
130
|
+
Rails.root.join('rules'),
|
131
|
+
Rails.root.join('policies')
|
132
|
+
]
|
133
|
+
|
134
|
+
puts " 🔍 DEBUG: Search paths: #{search_paths.map(&:to_s)}"
|
135
|
+
|
136
|
+
search_paths.each do |search_path|
|
137
|
+
puts " 🔍 DEBUG: Checking search path: #{search_path}"
|
138
|
+
puts " 🔍 DEBUG: Directory exists? #{Dir.exist?(search_path)}"
|
139
|
+
|
140
|
+
next unless Dir.exist?(search_path)
|
141
|
+
|
142
|
+
puts " 🔍 DEBUG: Searching for documents in: #{search_path}"
|
143
|
+
found_files = Dir.glob(File.join(search_path, '**/*.{md,txt,yml,yaml}'))
|
144
|
+
puts " 🔍 DEBUG: Found #{found_files.length} files: #{found_files.map { |f| File.basename(f) }}"
|
145
|
+
|
146
|
+
found_files.each do |doc_path|
|
147
|
+
puts " 🔍 DEBUG: Checking file: #{doc_path}"
|
148
|
+
puts " 🔍 DEBUG: File readable? #{File.readable?(doc_path)}"
|
149
|
+
|
150
|
+
next unless File.readable?(doc_path)
|
151
|
+
|
152
|
+
content = File.read(doc_path)
|
153
|
+
puts " 🔍 DEBUG: File content preview: #{content[0..100]}..."
|
154
|
+
|
155
|
+
if content.match?(/business rule|rule|policy/i)
|
156
|
+
puts " 🔍 DEBUG: File matches business rule pattern!"
|
157
|
+
relevance = class_name && method_name ? calculate_relevance(content, class_name, method_name) : 0.5
|
158
|
+
documents << {
|
159
|
+
path: doc_path,
|
160
|
+
type: 'business_rule',
|
161
|
+
content: content,
|
162
|
+
relevance: relevance
|
163
|
+
}
|
164
|
+
else
|
165
|
+
puts " 🔍 DEBUG: File does NOT match business rule pattern"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
puts " 🔍 DEBUG: Total business rule documents found: #{documents.length}"
|
171
|
+
documents.sort_by { |doc| -doc[:relevance] }
|
172
|
+
end
|
173
|
+
|
174
|
+
def load_error_handling_documents(file_path, class_name = nil, method_name = nil)
|
175
|
+
documents = []
|
176
|
+
|
177
|
+
# Look for error handling documentation
|
178
|
+
search_paths = [
|
179
|
+
File.dirname(file_path),
|
180
|
+
Rails.root.join('docs'),
|
181
|
+
Rails.root.join('error_handling'),
|
182
|
+
Rails.root.join('troubleshooting')
|
183
|
+
]
|
184
|
+
|
185
|
+
search_paths.each do |search_path|
|
186
|
+
next unless Dir.exist?(search_path)
|
187
|
+
|
188
|
+
Dir.glob(File.join(search_path, '**/*.{md,txt,yml,yaml}')).each do |doc_path|
|
189
|
+
next unless File.readable?(doc_path)
|
190
|
+
|
191
|
+
content = File.read(doc_path)
|
192
|
+
if content.match?(/error|exception|failure|nil|null/i)
|
193
|
+
relevance = class_name && method_name ? calculate_relevance(content, class_name, method_name) : 0.5
|
194
|
+
documents << {
|
195
|
+
path: doc_path,
|
196
|
+
type: 'error_handling',
|
197
|
+
content: content,
|
198
|
+
relevance: relevance
|
199
|
+
}
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
documents.sort_by { |doc| -doc[:relevance] }
|
205
|
+
end
|
206
|
+
|
207
|
+
def load_user_experience_documents(file_path, class_name = nil, method_name = nil)
|
208
|
+
documents = []
|
209
|
+
|
210
|
+
# Look for UX documentation
|
211
|
+
search_paths = [
|
212
|
+
File.dirname(file_path),
|
213
|
+
Rails.root.join('docs'),
|
214
|
+
Rails.root.join('ux'),
|
215
|
+
Rails.root.join('user_experience')
|
216
|
+
]
|
217
|
+
|
218
|
+
search_paths.each do |search_path|
|
219
|
+
next unless Dir.exist?(search_path)
|
220
|
+
|
221
|
+
Dir.glob(File.join(search_path, '**/*.{md,txt,yml,yaml}')).each do |doc_path|
|
222
|
+
next unless File.readable?(doc_path)
|
223
|
+
|
224
|
+
content = File.read(doc_path)
|
225
|
+
if content.match?(/user experience|ux|user interface|ui/i)
|
226
|
+
relevance = class_name && method_name ? calculate_relevance(content, class_name, method_name) : 0.5
|
227
|
+
documents << {
|
228
|
+
path: doc_path,
|
229
|
+
type: 'user_experience',
|
230
|
+
content: content,
|
231
|
+
relevance: relevance
|
232
|
+
}
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
documents.sort_by { |doc| -doc[:relevance] }
|
238
|
+
end
|
239
|
+
|
240
|
+
def load_domain_specific_context(class_name, method_name)
|
241
|
+
# Load domain-specific business context
|
242
|
+
domain = determine_domain(class_name)
|
243
|
+
|
244
|
+
case domain
|
245
|
+
when 'order_management'
|
246
|
+
load_order_management_context(class_name, method_name)
|
247
|
+
when 'user_management'
|
248
|
+
load_user_management_context(class_name, method_name)
|
249
|
+
when 'payment_processing'
|
250
|
+
load_payment_processing_context(class_name, method_name)
|
251
|
+
when 'calculator_operations'
|
252
|
+
load_calculator_context(class_name, method_name)
|
253
|
+
else
|
254
|
+
load_general_context(class_name, method_name)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def load_order_management_context(class_name, method_name)
|
259
|
+
context = {}
|
260
|
+
|
261
|
+
if method_name.to_s.include?('validate')
|
262
|
+
context[:validation_rules] = [
|
263
|
+
'All order items must have valid price and quantity',
|
264
|
+
'Validation errors should provide clear user feedback',
|
265
|
+
'System should not crash due to invalid data'
|
266
|
+
]
|
267
|
+
end
|
268
|
+
|
269
|
+
context
|
270
|
+
end
|
271
|
+
|
272
|
+
def load_calculator_context(class_name, method_name)
|
273
|
+
context = {}
|
274
|
+
|
275
|
+
if method_name.to_s.include?('divide')
|
276
|
+
context[:division_rules] = [
|
277
|
+
'Division by zero should be handled gracefully',
|
278
|
+
'Return appropriate fallback values',
|
279
|
+
'Log division errors for audit purposes'
|
280
|
+
]
|
281
|
+
end
|
282
|
+
|
283
|
+
context
|
284
|
+
end
|
285
|
+
|
286
|
+
def load_general_context(class_name, method_name)
|
287
|
+
{
|
288
|
+
general_rules: [
|
289
|
+
'Errors should not crash the system',
|
290
|
+
'Provide meaningful error messages',
|
291
|
+
'Maintain system stability'
|
292
|
+
]
|
293
|
+
}
|
294
|
+
end
|
295
|
+
|
296
|
+
def calculate_relevance(content, class_name, method_name)
|
297
|
+
relevance = 0.0
|
298
|
+
|
299
|
+
# Check for class name mentions
|
300
|
+
if content.downcase.include?(class_name.downcase)
|
301
|
+
relevance += 0.4
|
302
|
+
end
|
303
|
+
|
304
|
+
# Check for method name mentions
|
305
|
+
if content.downcase.include?(method_name.to_s.downcase)
|
306
|
+
relevance += 0.3
|
307
|
+
end
|
308
|
+
|
309
|
+
# Check for related terms
|
310
|
+
related_terms = ['error', 'exception', 'nil', 'validation', 'calculation']
|
311
|
+
related_terms.each do |term|
|
312
|
+
if content.downcase.include?(term)
|
313
|
+
relevance += 0.1
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
relevance
|
318
|
+
end
|
319
|
+
|
320
|
+
def determine_domain(class_name)
|
321
|
+
case class_name.to_s.downcase
|
322
|
+
when /order|payment|invoice/
|
323
|
+
'order_management'
|
324
|
+
when /user|auth|session/
|
325
|
+
'user_management'
|
326
|
+
when /payment|transaction|gateway/
|
327
|
+
'payment_processing'
|
328
|
+
when /calculator|math|compute/
|
329
|
+
'calculator_operations'
|
330
|
+
else
|
331
|
+
'general'
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def analyze_error_against_business_rules(error, business_context, class_name, method_name)
|
336
|
+
analysis = {
|
337
|
+
error_type: error.class.name,
|
338
|
+
error_message: error.message,
|
339
|
+
business_impact: 'unknown',
|
340
|
+
user_experience_impact: 'unknown',
|
341
|
+
compliance_issues: [],
|
342
|
+
recommended_actions: []
|
343
|
+
}
|
344
|
+
|
345
|
+
# Analyze business impact
|
346
|
+
analysis[:business_impact] = assess_business_impact(error, business_context, class_name, method_name)
|
347
|
+
|
348
|
+
# Analyze user experience impact
|
349
|
+
analysis[:user_experience_impact] = assess_user_experience_impact(error, business_context, class_name, method_name)
|
350
|
+
|
351
|
+
# Check compliance with business rules
|
352
|
+
analysis[:compliance_issues] = check_compliance(error, business_context, class_name, method_name)
|
353
|
+
|
354
|
+
# Generate recommended actions
|
355
|
+
analysis[:recommended_actions] = generate_recommended_actions(error, business_context, class_name, method_name)
|
356
|
+
|
357
|
+
analysis
|
358
|
+
end
|
359
|
+
|
360
|
+
def assess_business_impact(error, business_context, class_name, method_name)
|
361
|
+
error_text = error.message.downcase
|
362
|
+
|
363
|
+
if error_text.include?('nil') || error_text.include?('null')
|
364
|
+
# Check if this is a critical business operation
|
365
|
+
if method_name.to_s.include?('calculate') || method_name.to_s.include?('total')
|
366
|
+
return 'high' # Critical business operation
|
367
|
+
else
|
368
|
+
return 'medium' # Standard operation
|
369
|
+
end
|
370
|
+
elsif error_text.include?('division') || error_text.include?('zero')
|
371
|
+
return 'medium' # Mathematical operation error
|
372
|
+
else
|
373
|
+
return 'low' # General error
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
def assess_user_experience_impact(error, business_context, class_name, method_name)
|
378
|
+
error_text = error.message.downcase
|
379
|
+
|
380
|
+
if error_text.include?('nil') || error_text.include?('null')
|
381
|
+
return 'high' # User will see broken functionality
|
382
|
+
elsif error_text.include?('division') || error_text.include?('zero')
|
383
|
+
return 'medium' # Mathematical error, may be confusing
|
384
|
+
else
|
385
|
+
return 'low' # General error
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
def check_compliance(error, business_context, class_name, method_name)
|
390
|
+
compliance_issues = []
|
391
|
+
|
392
|
+
# Check against business rules
|
393
|
+
business_context[:business_rules].each do |rule|
|
394
|
+
if rule[:content].downcase.include?('must') || rule[:content].downcase.include?('shall')
|
395
|
+
if !rule[:content].downcase.include?('error') && !rule[:content].downcase.include?('exception')
|
396
|
+
compliance_issues << "Business rule violation: #{rule[:content][0..100]}..."
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
compliance_issues
|
402
|
+
end
|
403
|
+
|
404
|
+
def generate_recommended_actions(error, business_context, class_name, method_name)
|
405
|
+
actions = []
|
406
|
+
|
407
|
+
# Analyze error type and suggest actions
|
408
|
+
if error.message.downcase.include?('nil') || error.message.downcase.include?('null')
|
409
|
+
actions << 'Implement nil value validation'
|
410
|
+
actions << 'Add default value handling'
|
411
|
+
actions << 'Provide user-friendly error messages'
|
412
|
+
end
|
413
|
+
|
414
|
+
if error.message.downcase.include?('division') || error.message.downcase.include?('zero')
|
415
|
+
actions << 'Add division by zero protection'
|
416
|
+
actions << 'Implement mathematical error handling'
|
417
|
+
actions << 'Return appropriate fallback values'
|
418
|
+
end
|
419
|
+
|
420
|
+
# Add business rule specific actions
|
421
|
+
business_context[:business_rules].each do |rule|
|
422
|
+
if rule[:content].downcase.include?('return') && rule[:content].downcase.include?('nil')
|
423
|
+
actions << "Apply business rule: #{rule[:content][0..100]}..."
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
actions.uniq
|
428
|
+
end
|
429
|
+
|
430
|
+
def determine_evolution_strategy(analysis_result, error, class_name, method_name)
|
431
|
+
strategy = {
|
432
|
+
strategy: 'standard_error_handling',
|
433
|
+
business_impact: analysis_result[:business_impact],
|
434
|
+
user_experience_impact: analysis_result[:user_experience_impact],
|
435
|
+
priority: 'normal',
|
436
|
+
approach: 'defensive_programming'
|
437
|
+
}
|
438
|
+
|
439
|
+
# Determine strategy based on business impact
|
440
|
+
case analysis_result[:business_impact]
|
441
|
+
when 'high'
|
442
|
+
strategy[:strategy] = 'business_critical_fix'
|
443
|
+
strategy[:priority] = 'high'
|
444
|
+
strategy[:approach] = 'robust_error_handling'
|
445
|
+
when 'medium'
|
446
|
+
strategy[:strategy] = 'enhanced_error_handling'
|
447
|
+
strategy[:priority] = 'medium'
|
448
|
+
strategy[:approach] = 'graceful_degradation'
|
449
|
+
when 'low'
|
450
|
+
strategy[:strategy] = 'standard_error_handling'
|
451
|
+
strategy[:priority] = 'low'
|
452
|
+
strategy[:approach] = 'basic_error_handling'
|
453
|
+
end
|
454
|
+
|
455
|
+
# Adjust based on user experience impact
|
456
|
+
if analysis_result[:user_experience_impact] == 'high'
|
457
|
+
strategy[:approach] = 'user_experience_focused'
|
458
|
+
end
|
459
|
+
|
460
|
+
strategy
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|