diff --git a/crates/searchi-service/src/coordination.rs b/crates/searchi-service/src/coordination.rs index a1bf6d5..44b4ffd 100644 --- a/crates/searchi-service/src/coordination.rs +++ b/crates/searchi-service/src/coordination.rs @@ -301,6 +301,70 @@ impl EmbeddingProvider for BudgetedProvider { mod tests { use super::*; #[test] + fn failed_admission_clears_prior_worker_diagnostics_without_calling_worker() { + use searchi_core::metrics::MetricsMode; + use std::sync::atomic::{AtomicUsize, Ordering}; + struct ObservedInvocation(Arc); + impl EmbeddingProvider for ObservedInvocation { + fn fingerprint(&self) -> &str { + "scheduler-test-only" + } + fn dimensions(&self) -> usize { + 0 + } + fn split_windows(&mut self, _: &str, _: &str) -> Result> { + unreachable!() + } + fn embed_documents(&mut self, _: &[&str], _: &str) -> Result>> { + unreachable!() + } + fn embed_query(&mut self, _: &str) -> Result> { + self.0.fetch_add(1, Ordering::SeqCst); + Ok(Vec::new()) + } + fn embed_code_query(&mut self, query: &str) -> Result> { + self.embed_query(query) + } + fn query_diagnostics(&self) -> BTreeMap { + BTreeMap::from([("prior_worker_call".into(), 1)]) + } + } + let budget = InferenceBudget::new(1); + // Test worker weight two without requiring particular test host hardware. + budget.0.state.lock().unwrap().limit = 2; + let calls = Arc::new(AtomicUsize::new(0)); + let mut provider = BudgetedProvider::new( + Box::new(ObservedInvocation(calls.clone())), + &budget, + InferenceRole::Foreground, + 2, + ); + provider + .embed_query_debug("first admitted invocation") + .unwrap(); + assert_eq!(calls.load(Ordering::SeqCst), 1); + assert_eq!(provider.query_diagnostics()["prior_worker_call"], 1); + assert_eq!(budget.configure(1), 1); + let mut trace = TraceCollector::new(MetricsMode::Debug); + assert!(provider + .embed_query_traced("rejected invocation", true, &mut trace, None) + .is_err()); + assert_eq!(calls.load(Ordering::SeqCst), 1); + assert!(provider.query_diagnostics().is_empty()); + let metrics = trace.finish(0); + let stages = metrics.trace.unwrap(); + assert_eq!(stages.len(), 1); + assert_eq!(stages[0].name, "semantic.inference.admission_wait"); + assert_eq!(stages[0].counters["failed"], 1); + assert_eq!(stages[0].counters["worker_threads"], 2); + assert!(stages[0].duration_ms >= 0.0); + assert!(provider.embed_code_query_debug("also rejected").is_err()); + assert!(provider.query_diagnostics().is_empty()); + assert_eq!(calls.load(Ordering::SeqCst), 1); + assert_eq!(budget.0.state.lock().unwrap().foreground, 0); + assert_eq!(budget.0.state.lock().unwrap().used, 0); + } + #[test] fn reserved_query_capacity_admits_foreground_during_background() { let budget = InferenceBudget::new(1); // Deliberately exercise a two-thread scheduler independent of CI hardware. diff --git a/docs/RECOVERY-AND-HANDOFF.md b/docs/RECOVERY-AND-HANDOFF.md index 8f9bd44..075a573 100644 --- a/docs/RECOVERY-AND-HANDOFF.md +++ b/docs/RECOVERY-AND-HANDOFF.md @@ -87,6 +87,31 @@ installers/upgrades/signatures and macOS packaging remain open. ## Executed checks +The scheduling change at `d85746d` passed 110 Windows workspace tests (19 +explicitly ignored), 20 desktop unit tests and 39 frontend tests. Workspace and +desktop strict Clippy, Rust formatting, frontend production build and Prettier +checks passed. The real Google concurrent-inference regression passed again +after scoped-thread cleanup; its measured scope is documented in +[PERFORMANCE.md](PERFORMANCE.md#shared-inference-admission). + +The updated Windows embedded-frontend Release executable is 23,523,328 bytes, +SHA-256 `6efc81b653c7bd37ecbe63bbe538b126440daa5b62419dd1994c4a3696799d52`. +Its unsigned development ZIP is 145,262,551 bytes, SHA-256 +`c1859d6ead7628f02965568e1c509cd52484e22ec397a86f62bcea108487de16`. +The extracted archive passed integrity checks for 39 files, genuine 768-dimensional +normalized Google code-query inference and decoder startup. It requested automatic +device selection with Vulkan disabled only in the smoke-test child, used the +optimized CPU module and loaded no Vulkan provider. This does not verify the +updated desktop UI, clean-machine installation or actual GPU behavior. + +Five targeted scheduler tests also passed, including lowering admission below +a session's worker weight: rejection invokes no worker, clears prior diagnostics, +closes the failure trace and releases priority reservations. This is scheduler +failure testing; semantic quality is covered by the separate real Google fixture. + +The following checks describe the earlier prepared-reader/recovery milestone, +not the updated Linux desktop or updated Windows release artifacts: + - Windows and native Linux workspace tests: 106 passed each, 18 ignored. - Windows native desktop unit suite: 17 passed. - Strict Windows/Linux workspace and Windows desktop Clippy plus both formatting checks passed.