Validate gated Google retrieval and record unchanged localization quality

This commit is contained in:
karl committed 2026-10-13 13:49:23 +02:00
1 parent af04cec27c
commit 59908adfa6
5 files changed
+267 -5

No files matched your search

+3
View File
@@ -82,6 +82,9 @@ release. Keep their evidence and unchecked production gates intact.
- [x] Build a pinned CPU VAD diagnostic with explicit one-thread inference, - [x] Build a pinned CPU VAD diagnostic with explicit one-thread inference,
native error detection and full probabilities; actual Silero silence/input native error detection and full probabilities; actual Silero silence/input
contracts passed. Media quality comparison remains open (see SPEECH-VAD.md). contracts passed. Media quality comparison remains open (see SPEECH-VAD.md).
- [x] Complete frozen five-source VAD/base-ASR/Google CPU diagnostic:
component costs decreased, but localization stayed 4/8 point and 6/8 overlap;
no production default selected. Independent held-out media quality remains open.
- [ ] Run genuine local speech candidates, Google transcript retrieval and - [ ] Run genuine local speech candidates, Google transcript retrieval and
held-out English/German/music/no-speech checks before selecting a default. held-out English/German/music/no-speech checks before selecting a default.
- [ ] Package and verify decoders on every release OS; test production acceleration. - [ ] Package and verify decoders on every release OS; test production acceleration.
+35 -2
View File
@@ -243,11 +243,44 @@ segments versus nineteen; segment count does not prove transcript accuracy.
Music/sound ASR text vanished through explicit no-speech decisions, not manual Music/sound ASR text vanished through explicit no-speech decisions, not manual
phrase filtering. Original Google audio retrieval remains independent. phrase filtering. Original Google audio retrieval remains independent.
The next gate is genuine Google retrieval on all eight unchanged queries and Genuine Google retrieval on all eight unchanged queries is now measured below;
independent speech recall/alignment assessment. Do not select a production independent speech recall/alignment assessment remains open. Do not select a production
policy from timing or this tiny corpus alone. A future persistent ASR session policy from timing or this tiny corpus alone. A future persistent ASR session
could reduce model-start overhead but must be measured independently. could reduce model-start overhead but must be measured independently.
## Genuine Google comparison result
The updated Rust diagnostic verifies the exact embedded frozen VAD bytes,
reconstructs every source interval window and requires explicit no-speech status
for the four empty competitors. The original ungated coverage contract remains
strict. Six Rust tests passed, including omission/tampering rejection; the
agent also ran strict Clippy and built the release executable. Its recorded
SHA-256 matches the current executable:
`5a8ecb9e2ff571610cfe9f0dcd4876953d8975ff4a84972d321cd91636ceaf84`.
Actual Google EmbeddingGemma 2 CPU document/query inference completed using the
normal auto-capable worker with CPU forced. All twenty recognized segments
entered retrieval without relevance-label filtering; all five original source
identities remained verified. The complete, eligible ranking report is ignored
`.searchi/speech/gated-base-google-ranks-v2.json`, SHA-256
`1647cf5826dbeaf43f8c47209ca40af6bc2b68cd56f0c5f44eda36ed2eab8a30`.
The scorer report SHA-256 is
`3d15e72764cf2329b31caf9020af9182a91acb7229b13d7dec1b24464351c0dc`.
An initial run used a nonexistent worker path and retained its incomplete
verification checkpoint; it performed no successful inference and was not scored.
| Fixed eight-query metric | Ungated base | VAD-gated base |
| --- | ---: | ---: |
| Correct top file | 8/8 | 8/8 |
| Top start point within labelled interval | 4/8 | 4/8 |
| Top interval overlaps labelled interval | 6/8 | 6/8 |
The speech gate removed incidental music/sound transcripts and reduced this
pool's processing cost, but did **not** improve these localization metrics.
It is not selected as a production default. More independent speech coverage,
recognition and alignment work is needed before adding transcription enrichment.
The original Google media embeddings remain the product's media search path.
Open gates: actual one-thread audit, detection/recognition Open gates: actual one-thread audit, detection/recognition
comparison, ordinary and DTW timestamp regression, independent held-out quality, comparison, ordinary and DTW timestamp regression, independent held-out quality,
background starvation/cancellation/crash recovery, packaging/license review, background starvation/cancellation/crash recovery, packaging/license review,
+11
View File
@@ -19,6 +19,17 @@ ranks are retained; at most the first 100 segment hits per query are exported fo
the existing scorer. Ties use stable source order. No confidence threshold or the existing scorer. Ties use stable source order. No confidence threshold or
oracle matching is applied. oracle matching is applied.
The additional frozen `gated-vad-v1` diagnostic policy accepts only the exact
VAD report SHA-256 `093be2005b57fe16a8f85161cc9da8966e70e2050cf19fa4122531e304dd70df`.
The ASR report embeds its original UTF-8 bytes and parsed document; both must
agree. Source probes, decoder hashes and the pinned base Whisper worker must
match. The ranker independently rebuilds every contiguous window from the frozen
bounded source-sample intersections, retaining interval identity and explicit
sub-millisecond tail remainder. Omitted intervals, changed windows or claimed
no-speech status without frozen empty intervals are rejected before inference.
All five sources remain explicit competitors; no relevance label selects which
source receives ASR. Existing ungated reports still require full audio coverage.
```powershell ```powershell
cargo test --manifest-path tools/speech-rank/Cargo.toml --locked cargo test --manifest-path tools/speech-rank/Cargo.toml --locked
cargo build --release --manifest-path tools/speech-rank/Cargo.toml --locked cargo build --release --manifest-path tools/speech-rank/Cargo.toml --locked
+193
View File
@@ -0,0 +1,193 @@
//! Gated diagnostics are anchored to exact frozen VAD bytes, never claimed windows.
use super::*;
pub const POLICY: &str = "gated-vad-v1;source-intersections;integer-ms-tail-remainder-recorded;contiguous30s;independent-process;auto-language;segment-times";
const VAD_SHA: &str = "093be2005b57fe16a8f85161cc9da8966e70e2050cf19fa4122531e304dd70df";
pub fn verified_report(asr: &Value) -> Result<Option<Value>> {
if asr["policy"] != POLICY {
return Ok(None);
}
let bytes = asr["vad_report_utf8"]
.as_str()
.context("Missing original VAD bytes")?
.as_bytes();
ensure!(
bytes.len() <= 16 * 1024 * 1024
&& format!("{:x}", Sha256::digest(bytes)) == VAD_SHA
&& asr["vad_report_sha256"] == VAD_SHA,
"Frozen VAD bytes changed"
);
let vad: Value = serde_json::from_slice(bytes)?;
ensure!(
asr["actual_vad_report"] == vad,
"VAD parsed provenance differs from original bytes"
);
ensure!(
asr["candidate"] == "base"
&& asr["worker_sha256"]
== "12c681b4bcd9c7679b647b9a041e121a2f9e72cb2a9c6f189f529ec8b79c36f8",
"Unknown gated ASR worker/model"
);
for name in ["ffmpeg_sha256", "ffprobe_sha256"] {
ensure!(asr[name] == vad[name], "VAD and ASR decoder differ");
}
Ok(Some(vad))
}
pub fn planned_windows(vad: &Value, row: &Value, asset: &Value) -> Result<Vec<Value>> {
let detected = vad["rows"]
.as_array()
.context("Missing frozen VAD rows")?
.iter()
.find(|v| v["asset"] == row["asset"])
.context("Missing VAD competitor")?;
ensure!(
detected["source_sha256"] == asset["sha256"] && row["actual_probe"] == detected["probe"],
"VAD source/probe differs"
);
let audio = detected["probe"]["streams"]
.as_array()
.context("Missing audio probe")?
.iter()
.find(|v| v["codec_type"] == "audio")
.context("Missing primary audio")?;
let start: f64 = audio["start_time"].as_str().unwrap_or("0").parse()?;
let duration: f64 = audio["duration"]
.as_str()
.context("Missing audio duration")?
.parse()?;
ensure!(
start.is_finite()
&& start >= 0.0
&& duration.is_finite()
&& duration > 0.0
&& duration <= 600.0,
"Invalid source timeline"
);
let origin = (start * 1000.0).ceil() as u64;
let end = ((start + duration) * 1000.0).ceil() as u64;
ensure!(
row["audio_start_ms"] == origin
&& row["audio_end_ms"] == end
&& row["audio_stream_index"] == audio["index"],
"ASR timeline differs from frozen probe"
);
let samples = number(&detected["native"]["samples"])?;
let intervals = detected["intervals"]
.as_array()
.context("Missing VAD intervals")?;
let mut result = Vec::new();
let mut previous = 0;
for (index, interval) in intervals.iter().enumerate() {
let raw_start = number(&interval["raw_cs"][0])?
.checked_mul(160)
.context("Interval overflow")?;
let raw_end = number(&interval["raw_cs"][1])?
.checked_mul(160)
.context("Interval overflow")?;
let first = raw_start.min(samples);
let last = raw_end.min(samples);
ensure!(
raw_end > raw_start
&& first >= previous
&& interval["source_samples"] == json!([first, last])
&& interval["tail_padding_samples"] == raw_end.saturating_sub(samples),
"Invalid bounded VAD intersection"
);
previous = last;
let mut offset = origin + first / 16;
let end = origin + last / 16;
while offset < end {
let duration = (end - offset).min(30000);
result.push(json!({"offset_ms":offset,"duration_ms":duration,"vad_interval_index":index,"vad_source_intersection":interval,"unrepresented_tail_samples":last%16}));
ensure!(result.len() <= 20, "Too many gated windows");
offset += duration;
}
}
ensure!(
row["status"]
== if result.is_empty() {
"no_speech"
} else {
"speech"
}
&& row["asr_not_run"] == result.is_empty(),
"Missing explicit speech/no-speech evidence"
);
Ok(result)
}
pub fn matches_plan(window: &Value, planned: &Value) -> bool {
[
"offset_ms",
"duration_ms",
"vad_interval_index",
"vad_source_intersection",
"unrepresented_tail_samples",
]
.iter()
.all(|key| window[key] == planned[key])
}
pub fn verify_windows(windows: &[Value], planned: &[Value]) -> Result<()> {
ensure!(
windows.len() == planned.len()
&& windows
.iter()
.zip(planned)
.all(|(window, plan)| matches_plan(window, plan)),
"Incomplete or changed VAD interval windows"
);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn source(intervals: Value) -> (Value, Value, Value) {
let probe = json!({"streams":[{"codec_type":"audio","index":2,"start_time":"0.125","duration":"65.0"}]});
let vad = json!({"rows":[{"asset":"clip04","source_sha256":"sha","probe":probe,"native":{"samples":1040000},"intervals":intervals}]});
let row = json!({"asset":"clip04","actual_probe":probe,"audio_start_ms":125,"audio_end_ms":65125,"audio_stream_index":2,"status":"speech","asr_not_run":false});
(vad, row, json!({"sha256":"sha"}))
}
#[test]
fn windows_reconstruct_original_intervals_without_merging() {
let intervals = json!([
{"raw_cs":[0,3100],"source_samples":[0,496000],"tail_padding_samples":0},
{"raw_cs":[4000,4200],"source_samples":[640000,672000],"tail_padding_samples":0}
]);
let (vad, row, asset) = source(intervals);
let plan = planned_windows(&vad, &row, &asset).unwrap();
assert_eq!(plan.len(), 3);
assert_eq!(plan[0]["offset_ms"], 125);
assert_eq!(plan[1]["offset_ms"], 30125);
assert_eq!(plan[1]["duration_ms"], 1000);
assert_eq!(plan[2]["offset_ms"], 40125);
assert!(verify_windows(&plan, &plan).is_ok());
assert!(verify_windows(&plan[..2], &plan).is_err());
let mut omitted = plan[2].clone();
omitted["vad_interval_index"] = json!(0);
assert!(!matches_plan(&omitted, &plan[2]));
let mut changed = vad.clone();
changed["rows"][0]["intervals"][0]["source_samples"][1] = json!(480000);
assert!(planned_windows(&changed, &row, &asset).is_err());
}
#[test]
fn no_speech_requires_empty_frozen_intervals_and_explicit_status() {
let (vad, mut row, asset) = source(json!([]));
assert!(planned_windows(&vad, &row, &asset).is_err());
row["status"] = json!("no_speech");
row["asr_not_run"] = json!(true);
assert!(planned_windows(&vad, &row, &asset).unwrap().is_empty());
row["audio_start_ms"] = json!(0);
assert!(planned_windows(&vad, &row, &asset).is_err());
}
#[test]
fn arbitrary_vad_claim_cannot_authorize_candidate_exclusion() {
let report = json!({"policy":POLICY,"vad_report_sha256":VAD_SHA,"vad_report_utf8":"{}","actual_vad_report":{}});
assert!(verified_report(&report).is_err());
assert!(verified_report(&json!({"policy":super::super::POLICY}))
.unwrap()
.is_none());
}
}
+25 -3
View File
@@ -13,6 +13,7 @@ use std::{
const FIXTURE_SHA: &str = "b4e465a0eb9582b826eebd2a20797e1680e4907e336d02d0ef2194e2ba0af393"; const FIXTURE_SHA: &str = "b4e465a0eb9582b826eebd2a20797e1680e4907e336d02d0ef2194e2ba0af393";
const ASSETS: [&str; 5] = ["clip04", "music01", "music02", "sound01", "sound02"]; const ASSETS: [&str; 5] = ["clip04", "music01", "music02", "sound01", "sound02"];
mod gated;
const POLICY: &str = "primary-audio-range-v3;aresample16000-before-atrim;samplecoverage-1ms;nonoverlapping30s;independent-process-per-window;auto-language;segment-times"; const POLICY: &str = "primary-audio-range-v3;aresample16000-before-atrim;samplecoverage-1ms;nonoverlapping30s;independent-process-per-window;auto-language;segment-times";
fn digest(path: &Path) -> Result<String> { fn digest(path: &Path) -> Result<String> {
@@ -90,7 +91,7 @@ fn eligible(asr: &Value, fixture: &Value) -> Result<(Vec<Value>, Vec<Value>)> {
ensure!( ensure!(
asr["complete"] == true asr["complete"] == true
&& asr["fixture_sha256"] == FIXTURE_SHA && asr["fixture_sha256"] == FIXTURE_SHA
&& asr["policy"] == POLICY && matches!(asr["policy"].as_str(), Some(POLICY) | Some(gated::POLICY))
&& asr["backend"] == "forced CPU" && asr["backend"] == "forced CPU"
&& asr["threads"] == 1, && asr["threads"] == 1,
"Incomplete, stale, or unrecognized ASR report" "Incomplete, stale, or unrecognized ASR report"
@@ -104,6 +105,7 @@ fn eligible(asr: &Value, fixture: &Value) -> Result<(Vec<Value>, Vec<Value>)> {
] { ] {
ensure!(asr[field].as_str().is_some_and(|hash| hash.len() == 64 && hash.bytes().all(|b| b.is_ascii_hexdigit())), "Missing ASR provenance"); ensure!(asr[field].as_str().is_some_and(|hash| hash.len() == 64 && hash.bytes().all(|b| b.is_ascii_hexdigit())), "Missing ASR provenance");
} }
let vad = gated::verified_report(asr)?;
let rows = asr["rows"].as_array().context("Missing ASR rows")?; let rows = asr["rows"].as_array().context("Missing ASR rows")?;
ensure!(rows.len() == 5, "All five competing assets required"); ensure!(rows.len() == 5, "All five competing assets required");
let mut seen = BTreeSet::new(); let mut seen = BTreeSet::new();
@@ -133,12 +135,23 @@ fn eligible(asr: &Value, fixture: &Value) -> Result<(Vec<Value>, Vec<Value>)> {
); );
let windows = row["windows"].as_array().context("Missing windows")?; let windows = row["windows"].as_array().context("Missing windows")?;
ensure!(windows.len() <= 20, "Oversized window list"); ensure!(windows.len() <= 20, "Oversized window list");
let planned = vad
.as_ref()
.map(|vad| gated::planned_windows(vad, row, asset))
.transpose()?;
if let Some(planned) = &planned {
gated::verify_windows(windows, planned)?;
}
let mut next = start; let mut next = start;
for (window_index, window) in windows.iter().enumerate() { for (window_index, window) in windows.iter().enumerate() {
let offset = number(&window["offset_ms"])?; let offset = number(&window["offset_ms"])?;
let duration = number(&window["duration_ms"])?; let duration = number(&window["duration_ms"])?;
ensure!( ensure!(
offset == next && duration == (end - offset).min(30000) && duration > 0, if let Some(planned) = &planned {
gated::matches_plan(window, &planned[window_index])
} else {
offset == next && duration == (end - offset).min(30000) && duration > 0
},
"Missing, overlapping or truncated source windows" "Missing, overlapping or truncated source windows"
); );
next = offset.checked_add(duration).context("Window overflow")?; next = offset.checked_add(duration).context("Window overflow")?;
@@ -183,7 +196,10 @@ fn eligible(asr: &Value, fixture: &Value) -> Result<(Vec<Value>, Vec<Value>)> {
ensure!(segments.len() <= 5000, "Too many recognized segments"); ensure!(segments.len() <= 5000, "Too many recognized segments");
} }
} }
ensure!(next == end, "Incomplete source-window coverage"); ensure!(
planned.is_some() || next == end,
"Incomplete source-window coverage"
);
} }
ensure!( ensure!(
asr["quality_failed"] == !excluded.is_empty(), asr["quality_failed"] == !excluded.is_empty(),
@@ -197,6 +213,12 @@ fn eligible(asr: &Value, fixture: &Value) -> Result<(Vec<Value>, Vec<Value>)> {
== excluded.len(), == excluded.len(),
"ASR validation failure count inconsistent" "ASR validation failure count inconsistent"
); );
if vad.is_some() {
ensure!(
asr["comparison_eligible"] == excluded.is_empty(),
"Gated comparison status inconsistent"
);
}
Ok((segments, excluded)) Ok((segments, excluded))
} }