#![allow(clippy::disallowed_methods)]
use aprender::format::validation::{fail_no_validation_rules, Gate, PokaYoke, PokaYokeResult};
use aprender::format::{save, ModelType, SaveOptions};
use serde::{Deserialize, Serialize};
use std::fs;
#[derive(Debug, Clone, Serialize, Deserialize)]
struct AudioModel {
name: String,
has_filterbank: bool,
filterbank_max: f32,
encoder_layers: usize,
vocab_size: usize,
}
impl AudioModel {
fn new(name: &str) -> Self {
Self {
name: name.to_string(),
has_filterbank: false,
filterbank_max: 1.0,
encoder_layers: 0,
vocab_size: 0,
}
}
fn with_filterbank(mut self, max_value: f32) -> Self {
self.has_filterbank = true;
self.filterbank_max = max_value;
self
}
fn with_encoder_layers(mut self, count: usize) -> Self {
self.encoder_layers = count;
self
}
fn with_vocab_size(mut self, size: usize) -> Self {
self.vocab_size = size;
self
}
}
impl PokaYoke for AudioModel {
fn poka_yoke_validate(&self) -> PokaYokeResult {
let mut result = PokaYokeResult::new();
if self.has_filterbank {
result.add_gate(Gate::pass("filterbank_present", 20));
} else {
result.add_gate(Gate::fail(
"filterbank_present",
20,
"Fix: Embed Slaney-normalized filterbank via MelFilterbankData::mel_80()",
));
}
validate_filterbank_normalization(&mut result, self.has_filterbank, self.filterbank_max);
validate_encoder_layers(&mut result, self.encoder_layers);
validate_vocabulary(&mut result, self.vocab_size);
result
}
}
fn validate_filterbank_normalization(result: &mut PokaYokeResult, has_fb: bool, max: f32) {
if !has_fb {
result.add_gate(Gate::fail(
"filterbank_normalized",
30,
"Fix: Add filterbank first (see filterbank_present gate)",
));
} else if max < 0.1 {
result.add_gate(Gate::pass("filterbank_normalized", 30));
} else {
result.add_gate(Gate::fail(
"filterbank_normalized",
30,
format!("Fix: Apply 2.0/bandwidth normalization (max={max:.4}, expected <0.1)"),
));
}
}
fn validate_encoder_layers(result: &mut PokaYokeResult, layers: usize) {
if layers >= 4 {
result.add_gate(Gate::pass("encoder_layers", 25));
} else {
result.add_gate(Gate::fail(
"encoder_layers",
25,
format!("Fix: Model needs ≥4 encoder layers (has {layers})"),
));
}
}
fn validate_vocabulary(result: &mut PokaYokeResult, vocab: usize) {
if vocab > 0 {
result.add_gate(Gate::pass("vocabulary_size", 25));
} else {
result.add_gate(Gate::fail(
"vocabulary_size",
25,
"Fix: Set vocabulary size > 0 for tokenization",
));
}
}
fn main() {
println!("═══════════════════════════════════════════════════════════════════");
println!(" APR-POKA-001: Poka-Yoke Validation Demo");
println!("═══════════════════════════════════════════════════════════════════\n");
demo_perfect_model();
demo_partial_model();
demo_failing_model();
demo_gate_inspection();
demo_from_gates();
demo_fail_no_validation_rules();
println!("\n═══════════════════════════════════════════════════════════════════");
println!(" Demo Complete");
println!("═══════════════════════════════════════════════════════════════════");
}
fn demo_perfect_model() {
println!("┌─────────────────────────────────────────────────────────────────┐");
println!("│ Demo 1: Perfect Model (A+ Grade) │");
println!("└─────────────────────────────────────────────────────────────────┘\n");
let model = AudioModel::new("whisper-tiny-perfect")
.with_filterbank(0.05) .with_encoder_layers(4)
.with_vocab_size(51865);
let result = model.poka_yoke_validate();
print_validation_result(&result);
let temp_path = "/tmp/poka_yoke_demo_perfect.apr";
let options = SaveOptions::new()
.with_name("whisper-tiny-perfect")
.with_poka_yoke_result(&result);
match save(&model, ModelType::LinearRegression, temp_path, options) {
Ok(()) => {
println!(" ✅ Model saved successfully to {temp_path}");
println!(
" Quality score embedded in header byte 22: {}",
result.score
);
let _ = fs::remove_file(temp_path);
}
Err(e) => println!(" ❌ Save failed: {e}"),
}
println!();
}
fn demo_partial_model() {
println!("┌─────────────────────────────────────────────────────────────────┐");
println!("│ Demo 2: Partial Model (C Grade - Passing) │");
println!("└─────────────────────────────────────────────────────────────────┘\n");
let model = AudioModel::new("whisper-tiny-partial")
.with_filterbank(0.5) .with_encoder_layers(4)
.with_vocab_size(51865);
let result = model.poka_yoke_validate();
print_validation_result(&result);
if !result.failed_gates().is_empty() {
println!(" 📋 Error Summary:");
println!("{}", indent_lines(&result.error_summary(), " "));
}
let temp_path = "/tmp/poka_yoke_demo_partial.apr";
let options = SaveOptions::new()
.with_name("whisper-tiny-partial")
.with_poka_yoke_result(&result);
match save(&model, ModelType::LinearRegression, temp_path, options) {
Ok(()) => {
println!(" ✅ Model saved (with warnings) to {temp_path}");
let _ = fs::remove_file(temp_path);
}
Err(e) => println!(" ❌ Save failed: {e}"),
}
println!();
}
fn demo_failing_model() {
println!("┌─────────────────────────────────────────────────────────────────┐");
println!("│ Demo 3: Failing Model (F Grade - Jidoka Triggered) │");
println!("└─────────────────────────────────────────────────────────────────┘\n");
let model = AudioModel::new("whisper-broken");
let result = model.poka_yoke_validate();
print_validation_result(&result);
println!(" 📋 Error Summary (Actionable Fixes):");
println!("{}", indent_lines(&result.error_summary(), " "));
let temp_path = "/tmp/poka_yoke_demo_fail.apr";
let options = SaveOptions::new()
.with_name("whisper-broken")
.with_poka_yoke_result(&result);
println!("\n 🛑 Attempting to save model with quality_score=0...");
match save(&model, ModelType::LinearRegression, temp_path, options) {
Ok(()) => println!(" ✅ Model saved (unexpected!)"),
Err(e) => {
println!(" ❌ JIDOKA: Save refused!");
println!(" Error: {e}");
println!(" This is intentional - Poka-yoke prevents shipping broken models.");
}
}
println!();
}
fn demo_gate_inspection() {
println!("┌─────────────────────────────────────────────────────────────────┐");
println!("│ Demo 4: Gate Inspection │");
println!("└─────────────────────────────────────────────────────────────────┘\n");
let model = AudioModel::new("inspection-demo")
.with_filterbank(0.05)
.with_encoder_layers(2) .with_vocab_size(0);
let result = model.poka_yoke_validate();
println!(" Gate Details:");
println!(" {:─<65}", "");
println!(
" {:<20} {:<6} {:<6} {:<6} Error",
"Gate Name", "Passed", "Points", "Max"
);
println!(" {:─<65}", "");
for gate in &result.gates {
let status = if gate.passed { "✅" } else { "❌" };
let error = gate.error.as_deref().unwrap_or("-");
println!(
" {:20} {:6} {:6} {:6} {}",
gate.name,
status,
gate.points,
gate.max_points,
truncate_error(error, 30)
);
}
println!(" {:─<65}", "");
println!(
" Total Score: {}/{} Grade: {}",
result.score,
result.max_score,
result.grade()
);
println!();
}
fn demo_from_gates() {
println!("┌─────────────────────────────────────────────────────────────────┐");
println!("│ Demo 5: Bulk Construction with from_gates() │");
println!("└─────────────────────────────────────────────────────────────────┘\n");
let gates = vec![
Gate::pass("data_integrity", 25),
Gate::pass("model_architecture", 25),
Gate::fail("hyperparameters", 25, "Fix: learning_rate must be > 0"),
Gate::pass("output_shape", 25),
];
let result = PokaYokeResult::from_gates(gates);
println!(
" Created PokaYokeResult from {} gates in one call",
result.gates.len()
);
print_validation_result(&result);
println!();
}
fn demo_fail_no_validation_rules() {
println!("┌─────────────────────────────────────────────────────────────────┐");
println!("│ Demo 6: fail_no_validation_rules() Helper │");
println!("└─────────────────────────────────────────────────────────────────┘\n");
let result = fail_no_validation_rules();
println!(" Use this helper when model doesn't implement PokaYoke trait:");
print_validation_result(&result);
println!("\n 📋 Error Summary:");
println!("{}", indent_lines(&result.error_summary(), " "));
println!();
}
fn print_validation_result(result: &PokaYokeResult) {
let status = if result.passed() {
"✅ PASSED"
} else {
"❌ FAILED"
};
println!(" Validation: {status}");
println!(" Score: {}/100 (Grade: {})", result.score, result.grade());
println!(
" Gates: {} passed, {} failed",
result.gates.iter().filter(|g| g.passed).count(),
result.failed_gates().len()
);
}
fn indent_lines(text: &str, indent: &str) -> String {
text.lines()
.map(|line| format!("{indent}{line}"))
.collect::<Vec<_>>()
.join("\n")
}
fn truncate_error(error: &str, max_len: usize) -> String {
if error.len() <= max_len {
error.to_string()
} else {
format!("{}...", &error[..max_len - 3])
}
}