Class: Aidp::Harness::ZfcConditionDetector

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

Overview

ZFC-enabled wrapper for ConditionDetector

Delegates semantic analysis to AI when ZFC is enabled, falls back to legacy pattern matching when disabled or on AI failure.

Examples:

Basic usage

detector = ZfcConditionDetector.new(config, provider_factory)
if detector.is_rate_limited?(result)
  # Handle rate limit
end

See Also:

  • docs/ZFC_COMPLIANCE_ASSESSMENTdocs/ZFC_COMPLIANCE_ASSESSMENT.md
  • docs/ZFC_IMPLEMENTATION_PLANdocs/ZFC_IMPLEMENTATION_PLAN.md

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config, provider_factory: nil) ⇒ ZfcConditionDetector

Initialize ZFC condition detector

Parameters:



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/aidp/harness/zfc_condition_detector.rb', line 28

def initialize(config, provider_factory: nil)
  @config = config
  @legacy_detector = ConditionDetector.new

  # Create ProviderFactory if not provided and ZFC is enabled
  # Note: ConfigManager doesn't have zfc_enabled?, so we check respond_to? first
  if provider_factory.nil? && config.respond_to?(:zfc_enabled?) && config.zfc_enabled?
    require_relative "provider_factory"
    provider_factory = ProviderFactory.new
  end

  @ai_engine = AIDecisionEngine.new(config, provider_factory: provider_factory)

  # Statistics for A/B testing
  @stats = {
    zfc_calls: 0,
    legacy_calls: 0,
    zfc_fallbacks: 0,
    agreements: 0,
    disagreements: 0,
    zfc_total_cost: 0.0
  }
end

Instance Attribute Details

#ai_engineObject (readonly)

Returns the value of attribute ai_engine.



22
23
24
# File 'lib/aidp/harness/zfc_condition_detector.rb', line 22

def ai_engine
  @ai_engine
end

#configObject (readonly)

Returns the value of attribute config.



22
23
24
# File 'lib/aidp/harness/zfc_condition_detector.rb', line 22

def config
  @config
end

#legacy_detectorObject (readonly)

Returns the value of attribute legacy_detector.



22
23
24
# File 'lib/aidp/harness/zfc_condition_detector.rb', line 22

def legacy_detector
  @legacy_detector
end

#statsObject (readonly)

Returns the value of attribute stats.



22
23
24
# File 'lib/aidp/harness/zfc_condition_detector.rb', line 22

def stats
  @stats
end

Instance Method Details

#classify_error(error, context = {}) ⇒ Hash

Classify error using AI or legacy pattern matching

Parameters:

  • error (Exception, StandardError)

    Error to classify

  • context (Hash) (defaults to: {})

    Additional context (provider, model, etc.)

Returns:

  • (Hash)

    Classification with error_type, retryable, recommended_action



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
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/aidp/harness/zfc_condition_detector.rb', line 143

def classify_error(error, context = {})
  return @legacy_detector.classify_error(error) unless zfc_enabled?(:error_classification)

  begin
    # Build context for AI decision
    error_context = {
      error_message: error_to_text(error),
      context: context.to_s
    }

    # Ask AI to classify error
    ai_result = @ai_engine.decide(:error_classification,
      context: error_context,
      tier: zfc_tier(:error_classification),
      cache_ttl: zfc_cache_ttl(:error_classification))

    record_zfc_call(:error_classification, ai_result)

    # A/B test if enabled
    if ab_testing_enabled?
      legacy_result = @legacy_detector.classify_error(error)
      compare_error_results(ai_result, legacy_result)
    end

    # Only use AI result if confidence is high enough
    if ai_result[:confidence] >= confidence_threshold(:error_classification)
      # Convert AI result to legacy format
      {
        error: error,
        error_type: ai_result[:error_type].to_sym,
        retryable: ai_result[:retryable],
        recommended_action: ai_result[:recommended_action].to_sym,
        confidence: ai_result[:confidence],
        reasoning: ai_result[:reasoning],
        timestamp: Time.now,
        context: context,
        message: error&.message || "Unknown error"
      }
    else
      @legacy_detector.classify_error(error)
    end
  rescue => e
    Aidp.log_error("zfc_condition_detector", "ZFC error classification failed, falling back to legacy", {
      error: e.message,
      error_class: e.class.name,
      original_error: error&.class&.name
    })
    record_fallback(:error_classification)
    @legacy_detector.classify_error(error)
  end
end

#extract_questions(result) ⇒ Array<Hash>

Extract questions from result (delegates to legacy for now)

Parameters:

  • result (Hash)

    AI response

Returns:

  • (Array<Hash>)

    List of questions



125
126
127
# File 'lib/aidp/harness/zfc_condition_detector.rb', line 125

def extract_questions(result)
  @legacy_detector.extract_questions(result)
end

#extract_rate_limit_info(result, provider = nil) ⇒ Hash

Extract rate limit info (delegates to legacy for now)

Parameters:

  • result (Hash)

    AI response or error

  • provider (String, nil) (defaults to: nil)

    Provider name

Returns:

  • (Hash)

    Rate limit information



134
135
136
# File 'lib/aidp/harness/zfc_condition_detector.rb', line 134

def extract_rate_limit_info(result, provider = nil)
  @legacy_detector.extract_rate_limit_info(result, provider)
end

#is_rate_limited?(result, provider = nil) ⇒ Boolean

Check if result indicates rate limiting

Parameters:

  • result (Hash)

    AI response or error

  • provider (String, nil) (defaults to: nil)

    Provider name for context

Returns:

  • (Boolean)

    true if rate limited



57
58
59
60
61
62
# File 'lib/aidp/harness/zfc_condition_detector.rb', line 57

def is_rate_limited?(result, provider = nil)
  detect_condition(:is_rate_limited?, result, provider: provider) do |ai_result|
    ai_result[:condition] == "rate_limit" &&
      ai_result[:confidence] >= confidence_threshold(:condition_detection)
  end
end

#is_work_complete?(result, progress = nil) ⇒ Boolean

Check if work is complete

Parameters:

  • result (Hash)

    AI response

  • progress (Hash, nil) (defaults to: nil)

    Progress context

Returns:

  • (Boolean)

    true if work complete



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
# File 'lib/aidp/harness/zfc_condition_detector.rb', line 80

def is_work_complete?(result, progress = nil)
  return false unless result

  if zfc_enabled?(:completion_detection)
    begin
      # Build context for AI decision
      context = {
        response: result_to_text(result),
        task_description: progress&.dig(:task) || "general task"
      }

      # Ask AI if work is complete
      ai_result = @ai_engine.decide(:completion_detection,
        context: context,
        tier: zfc_tier(:completion_detection),
        cache_ttl: zfc_cache_ttl(:completion_detection))

      record_zfc_call(:completion_detection, ai_result)

      # A/B test if enabled
      if ab_testing_enabled?
        legacy_result = @legacy_detector.is_work_complete?(result, progress)
        compare_results(:is_work_complete, ai_result[:complete], legacy_result)
      end

      ai_result[:complete] &&
        ai_result[:confidence] >= confidence_threshold(:completion_detection)
    rescue => e
      Aidp.log_error("zfc_condition_detector", "ZFC completion detection failed, falling back to legacy", {
        error: e.message,
        error_class: e.class.name
      })
      record_fallback(:completion_detection)
      @legacy_detector.is_work_complete?(result, progress)
    end
  else
    @stats[:legacy_calls] += 1
    @legacy_detector.is_work_complete?(result, progress)
  end
end

#needs_user_feedback?(result) ⇒ Boolean

Check if result needs user feedback

Parameters:

  • result (Hash)

    AI response

Returns:

  • (Boolean)

    true if user feedback needed



68
69
70
71
72
73
# File 'lib/aidp/harness/zfc_condition_detector.rb', line 68

def needs_user_feedback?(result)
  detect_condition(:needs_user_feedback?, result, provider: nil) do |ai_result|
    ai_result[:condition] == "user_feedback_needed" &&
      ai_result[:confidence] >= confidence_threshold(:condition_detection)
  end
end

#statisticsHash

Get statistics summary

Returns:

  • (Hash)

    Statistics including accuracy, cost, performance



198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/aidp/harness/zfc_condition_detector.rb', line 198

def statistics
  total_calls = @stats[:zfc_calls] + @stats[:legacy_calls]
  return @stats.merge(total_calls: 0, accuracy: nil) if total_calls.zero?

  comparisons = @stats[:agreements] + @stats[:disagreements]
  accuracy = comparisons.zero? ? nil : (@stats[:agreements].to_f / comparisons * 100).round(2)

  @stats.merge(
    total_calls: total_calls,
    zfc_percentage: (@stats[:zfc_calls].to_f / total_calls * 100).round(2),
    accuracy: accuracy,
    fallback_rate: (@stats[:zfc_fallbacks].to_f / [@stats[:zfc_calls], 1].max * 100).round(2)
  )
end