diff --git a/.github/workflows/bench-advisory.yml b/.github/workflows/bench-advisory.yml index dc6dc79..dbe070e 100644 --- a/.github/workflows/bench-advisory.yml +++ b/.github/workflows/bench-advisory.yml @@ -37,10 +37,12 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Run benchmarks run: | - cargo bench -p chanora_audio \ + if ! cargo bench -p chanora_audio \ --bench realtime_capture \ --bench opus_codec \ - --bench resampler + --bench resampler; then + echo "::warning::Benchmark harness failed; continuing advisory workflow per SRS-218 clause 4" + fi - name: Emit current baseline JSON run: cargo run --example emit_baseline -p chanora_audio - name: Resolve merge-base baseline diff --git a/crates/chanora_audio/benches/realtime_capture.rs b/crates/chanora_audio/benches/realtime_capture.rs index 2e2871a..0549367 100644 --- a/crates/chanora_audio/benches/realtime_capture.rs +++ b/crates/chanora_audio/benches/realtime_capture.rs @@ -61,9 +61,26 @@ fn bench_capture_alloc_count(c: &mut Criterion) { let blocks_final = stats_final.total_blocks; let delta = blocks_final - blocks_warm; + // Sidecar file for §5 emit_baseline: criterion's own + // estimates.json carries the no-op closure timing, NOT the + // alloc count, so we write the canonical alloc-count value + // here and the emitter reads it directly. Write before the + // developer-facing assertion so the advisory CI can still report + // the regression after tolerating the bench process failure. + if let Ok(dir) = std::env::var("CARGO_TARGET_DIR") + .map(std::path::PathBuf::from) + .or_else(|_| std::env::current_dir().map(|d| d.join("target"))) + { + let path = dir.join("criterion").join("capture_alloc_count.sidecar"); + if let Some(parent) = path.parent() { + let _ = std::fs::create_dir_all(parent); + } + let _ = std::fs::write(&path, delta.to_string()); + } + // Local-developer surface: hard-fail on any regression. // The CI advisory comparator carries the same rule with a - // markdown 🔴 marker on regression instead of a panic. + // markdown marker on regression instead of a panic. assert_eq!( delta, 0, "post-warmup heap allocation regression: {} blocks (SRS-219 clause a)", @@ -76,27 +93,12 @@ fn bench_capture_alloc_count(c: &mut Criterion) { // measurement is the delta computed above; criterion's // function-time-mean is uninteresting for an alloc-count // metric. The §5 emitter reads the `capture_alloc_count` - // metric value out of band via a sidecar file written below. + // metric value out of band via the sidecar file written above. c.bench_function("capture_alloc_count", |b| { b.iter(|| { black_box(delta); }); }); - - // Sidecar file for §5 emit_baseline: criterion's own - // estimates.json carries the no-op closure timing, NOT the - // alloc count, so we write the canonical alloc-count value - // here and the emitter reads it directly. - if let Ok(dir) = std::env::var("CARGO_TARGET_DIR") - .map(std::path::PathBuf::from) - .or_else(|_| std::env::current_dir().map(|d| d.join("target"))) - { - let path = dir.join("criterion").join("capture_alloc_count.sidecar"); - if let Some(parent) = path.parent() { - let _ = std::fs::create_dir_all(parent); - } - let _ = std::fs::write(&path, delta.to_string()); - } } #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "android")))]