parlov-analysis 0.5.0

Analysis engine trait and signal detection for parlov.
Documentation
//! Classifies (baseline, probe) status code pairs as `OracleResult` values.
//!
//! `ExistenceAnalyzer` delegates to [`classify`] after extracting status codes and signals
//! from the `DifferentialSet`. The pattern table lives in the sibling `patterns` module,
//! and scoring logic lives in `scoring`.

use http::StatusCode;
use parlov_core::{OracleClass, OracleResult, Signal, Technique};

use super::patterns;
use super::scoring;

/// Classifies a (baseline, probe) status code pair with signal and technique context.
///
/// Three-layer scoring pipeline:
/// 1. Pattern table lookup for base confidence/impact.
/// 2. Per-signal weighted scoring with family adjustment and normative calibration.
/// 3. Confidence thresholds determine verdict; impact class gated by confidence gives severity.
///
/// Returns `NotPresent` when both codes are identical, and a fully scored result otherwise.
pub(super) fn classify(
    baseline: StatusCode,
    probe: StatusCode,
    signals: Vec<Signal>,
    technique: &Technique,
) -> OracleResult {
    let pattern = patterns::lookup(baseline, probe);
    let scoring_output = scoring::compute(
        &pattern,
        &signals,
        technique.strength,
        baseline.as_u16(),
    );

    OracleResult {
        class: OracleClass::Existence,
        verdict: scoring_output.verdict,
        severity: scoring_output.severity,
        confidence: scoring_output.confidence,
        impact_class: scoring_output.impact_class,
        reasons: scoring_output.reasons,
        signals,
        technique_id: Some(technique.id.to_string()),
        vector: Some(technique.vector),
        normative_strength: Some(technique.strength),
        label: pattern.label.map(String::from),
        leaks: pattern.leaks.map(String::from),
        rfc_basis: pattern.rfc_basis.map(String::from),
    }
}

#[cfg(test)]
#[path = "classifier_tests.rs"]
mod tests;