debtmap 0.16.4

Code complexity and technical debt analyzer
Documentation
//! Confidence thresholds for responsibility classification.
//!
//! This module defines the confidence thresholds used to determine when
//! a method's responsibility classification is reliable enough to act on.
//!
//! # Rationale
//!
//! Without confidence thresholds, the system tends to over-classify methods
//! as "utilities", leading to poor decomposition recommendations. By requiring
//! minimum confidence scores, we ensure only reliable classifications drive
//! architectural decisions.

/// Minimum confidence for any classification.
///
/// If a classification has confidence below this threshold, it is rejected
/// and the method is left in its original location rather than extracted.
///
/// **Value**: 0.50 (50%)
///
/// **Rationale**: Require more signal than noise before accepting a classification.
pub const MINIMUM_CONFIDENCE: f64 = 0.50;

/// Minimum confidence for "utilities" classification.
///
/// The "utilities" category requires higher confidence than other categories
/// because it was previously used as an unconditional fallback, leading to
/// over-classification (~30% of methods).
///
/// **Value**: 0.60 (60%)
///
/// **Rationale**: Higher bar prevents lazy fallback to "utilities" when
/// other signals are weak.
pub const UTILITIES_THRESHOLD: f64 = 0.60;

/// Minimum confidence for module split recommendation.
///
/// Module splits are structural changes that require high confidence.
/// Only recommend splits when aggregate confidence across all methods
/// in the cluster exceeds this threshold.
///
/// **Value**: 0.65 (65%)
///
/// **Rationale**: Structural changes need strong evidence to justify
/// the refactoring effort and risk.
pub const MODULE_SPLIT_CONFIDENCE: f64 = 0.65;

/// Minimum number of methods required for a module split.
///
/// **Value**: 5 methods
///
/// **Rationale**: Splitting off fewer methods creates unnecessary fragmentation.
pub const MIN_METHODS_FOR_SPLIT: usize = 5;

/// Metrics tracking classification results for observability.
///
/// Used to monitor classification quality and identify potential issues
/// with thresholds or classification logic.
#[derive(Debug, Clone, Default)]
pub struct ClassificationMetrics {
    /// Total number of methods attempted to classify
    pub total_methods: usize,
    /// Number of methods successfully classified with confidence above threshold
    pub classified_methods: usize,
    /// Number of methods that couldn't be classified (low confidence)
    pub unclassified_methods: usize,
    /// Number of methods classified as utilities
    pub utilities_count: usize,
}

impl ClassificationMetrics {
    /// Create a new metrics tracker
    pub fn new() -> Self {
        Self::default()
    }

    /// Record a classification attempt
    pub fn record_classification(&mut self, category: Option<&str>) {
        self.total_methods += 1;
        if let Some(cat) = category {
            self.classified_methods += 1;
            if cat == "utilities" {
                self.utilities_count += 1;
            }
        } else {
            self.unclassified_methods += 1;
        }
    }

    /// Calculate the utilities classification rate
    pub fn utilities_rate(&self) -> f64 {
        if self.total_methods == 0 {
            0.0
        } else {
            self.utilities_count as f64 / self.total_methods as f64
        }
    }

    /// Calculate the classification success rate
    pub fn classification_rate(&self) -> f64 {
        if self.total_methods == 0 {
            0.0
        } else {
            self.classified_methods as f64 / self.total_methods as f64
        }
    }
}

/// Emit classification metrics to logs for observability.
///
/// This function logs aggregated statistics about classification results,
/// which is useful for:
/// - Monitoring utilities classification rate (should be <10%)
/// - Identifying when thresholds may need tuning
/// - Detecting anomalies in classification behavior
pub fn emit_classification_metrics(metrics: &ClassificationMetrics) {
    log::info!(
        "Classification metrics: total={}, classified={}, unclassified={}, utilities={} ({:.1}%)",
        metrics.total_methods,
        metrics.classified_methods,
        metrics.unclassified_methods,
        metrics.utilities_count,
        metrics.utilities_rate() * 100.0
    );

    if metrics.utilities_rate() > 0.10 {
        log::warn!(
            "High utilities classification rate: {:.1}% (target: <10%)",
            metrics.utilities_rate() * 100.0
        );
    }

    if metrics.total_methods > 0 && metrics.classification_rate() < 0.50 {
        log::warn!(
            "Low classification rate: {:.1}% (may need to review thresholds)",
            metrics.classification_rate() * 100.0
        );
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_threshold_values() {
        // Document threshold values and relationships
        // These are compile-time constants, so we verify they exist
        let _min = MINIMUM_CONFIDENCE;
        let _util = UTILITIES_THRESHOLD;
        let _split = MODULE_SPLIT_CONFIDENCE;
        let _methods = MIN_METHODS_FOR_SPLIT;

        // Verify runtime behavior with dynamic values
        let test_confidence = 0.55;
        assert!(test_confidence >= MINIMUM_CONFIDENCE);
        assert!(test_confidence < UTILITIES_THRESHOLD);
    }
}