///|
/// Quality decision produced by a configurable acceptance policy.
pub(all) enum QualityDecision {
Pass
Warning(reason~ : String)
Fail(reason~ : String)
} derive(Eq, Debug)
///|
/// Thresholds used to turn measurements into operational decisions.
pub(all) struct AcceptancePolicy {
warning_uncertainty_ratio : Double
fail_uncertainty_ratio : Double
minimum_cpk : Double
minimum_samples : Int
allow_unmeasured : Bool
}
///|
/// Result of evaluating one value against a policy.
pub(all) struct ValueQualityResult {
feature_id : String
value : Double
normalized_deviation : Double
uncertainty : Double
decision : QualityDecision
}
///|
/// Result of evaluating a batch summary against a policy.
pub(all) struct BatchQualityResult {
feature_id : String
observed_count : Int
defect_rate : Double
cpk : Double
decision : QualityDecision
}
///|
/// Return a conservative default policy for offline acceptance screening.
pub fn default_acceptance_policy() -> AcceptancePolicy {
{
warning_uncertainty_ratio: 0.5,
fail_uncertainty_ratio: 1.0,
minimum_cpk: 1.0,
minimum_samples: 1,
allow_unmeasured: false,
}
}
///|
/// Create a policy with explicit thresholds.
pub fn acceptance_policy(
warning_uncertainty_ratio~ : Double,
fail_uncertainty_ratio~ : Double,
minimum_cpk~ : Double,
minimum_samples~ : Int,
allow_unmeasured~ : Bool,
) -> AcceptancePolicy {
{
warning_uncertainty_ratio,
fail_uncertainty_ratio,
minimum_cpk,
minimum_samples,
allow_unmeasured,
}
}
///|
fn decision_for_uncertainty(
policy : AcceptancePolicy,
ratio : Double,
) -> QualityDecision? {
if ratio > policy.fail_uncertainty_ratio {
Some(QualityDecision::Fail(reason="uncertainty exceeds policy limit"))
} else if ratio > policy.warning_uncertainty_ratio {
Some(QualityDecision::Warning(reason="uncertainty requires review"))
} else {
None
}
}
///|
/// Evaluate one observed value and its stated uncertainty.
pub fn evaluate_value(
policy~ : AcceptancePolicy,
feature~ : Feature,
value~ : Double,
uncertainty~ : Double,
) -> ValueQualityResult {
let normalized_deviation = relative_error(feature, value)
let tolerance = tolerance_half_width(feature)
let ratio = if tolerance == 0.0 {
if uncertainty == 0.0 {
0.0
} else {
policy.fail_uncertainty_ratio + 1.0
}
} else {
uncertainty.abs() / tolerance
}
let decision = if value < lower_limit(feature) || value > upper_limit(feature) {
QualityDecision::Fail(reason="value outside tolerance")
} else {
match decision_for_uncertainty(policy, ratio) {
Some(decision) => decision
None => QualityDecision::Pass
}
}
{ feature_id: feature.id, value, normalized_deviation, uncertainty, decision }
}
///|
/// Evaluate a complete batch summary.
pub fn evaluate_batch(
policy : AcceptancePolicy,
summary : BatchSummary,
) -> BatchQualityResult {
let decision = if summary.fail_count > 0 {
QualityDecision::Fail(reason="batch contains failed observations")
} else if summary.count < policy.minimum_samples && !policy.allow_unmeasured {
QualityDecision::Warning(reason="batch has too few observations")
} else if summary.capability.cpk < policy.minimum_cpk {
QualityDecision::Warning(
reason="batch capability is below policy threshold",
)
} else {
QualityDecision::Pass
}
{
feature_id: summary.feature_id,
observed_count: summary.count,
defect_rate: summary.defect_rate,
cpk: summary.capability.cpk,
decision,
}
}
///|
/// Return whether a policy result is acceptable for release.
pub fn policy_accepts(result : BatchQualityResult) -> Bool {
result.decision is QualityDecision::Pass
}
///|
/// Return whether a value result is a hard failure.
pub fn value_is_failure(result : ValueQualityResult) -> Bool {
result.decision is QualityDecision::Fail(_)
}
///|
/// Return whether a value result needs human review.
pub fn value_needs_review(result : ValueQualityResult) -> Bool {
result.decision is QualityDecision::Warning(_)
}
///|
/// Render a stable decision label.
pub fn quality_decision_text(decision : QualityDecision) -> String {
match decision {
Pass => "pass"
Warning(_) => "warning"
Fail(_) => "fail"
}
}
///|
/// Return a human-readable decision reason.
pub fn quality_decision_reason(decision : QualityDecision) -> String {
match decision {
Pass => "within policy"
Warning(reason~) => reason
Fail(reason~) => reason
}
}
///|
/// Return whether a policy is structurally usable.
pub fn policy_is_valid(policy : AcceptancePolicy) -> Bool {
policy.warning_uncertainty_ratio >= 0.0 &&
policy.fail_uncertainty_ratio >= policy.warning_uncertainty_ratio &&
policy.minimum_cpk >= 0.0 &&
policy.minimum_samples >= 0
}
///|
/// Count failures in a set of value results.
pub fn value_failure_count(results : Array[ValueQualityResult]) -> Int {
let mut count = 0
for result in results {
if value_is_failure(result) {
count += 1
}
}
count
}
///|
/// Count warnings in a set of value results.
pub fn value_warning_count(results : Array[ValueQualityResult]) -> Int {
let mut count = 0
for result in results {
if value_needs_review(result) {
count += 1
}
}
count
}