#![allow(clippy::cast_precision_loss, clippy::similar_names)]
mod common;
use beads_rust::util::hex_encode;
use common::artifact_validator::{
ArtifactValidator, PerfEvidenceBinary, PerfEvidenceCommand, PerfEvidenceComparison,
PerfEvidenceDataset, PerfEvidenceEnvVar, PerfEvidenceEnvironment, PerfEvidenceGit,
PerfEvidenceGolden, PerfEvidenceManifest, PerfEvidencePolicy, PerfEvidenceResources,
PerfEvidenceTiming, StartupMatrixAggregation, StartupMatrixManifest, StartupMatrixState,
};
use common::binary_discovery::{DiscoveredBinaries, discover_binaries};
use common::dataset_registry::{IsolatedDataset, KnownDataset};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::fs::{self, File};
use std::io::BufWriter;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::time::{Duration, Instant};
use tempfile::TempDir;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ColdWarmMetrics {
pub command: String,
pub binary: String,
pub cold_start_ms: u128,
pub warm_runs_ms: Vec<u128>,
pub warm_avg_ms: f64,
pub cold_warm_ratio: f64,
pub warm_std_dev_ms: f64,
pub success: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ColdWarmComparison {
pub command: String,
pub br: ColdWarmMetrics,
pub bd: ColdWarmMetrics,
pub cold_ratio_br_bd: f64,
pub warm_ratio_br_bd: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ColdWarmBenchmark {
pub dataset_name: String,
pub issue_count: usize,
pub comparisons: Vec<ColdWarmComparison>,
pub summary: ColdWarmSummary,
pub timestamp: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ColdWarmSummary {
pub br_avg_cold_warm_ratio: f64,
pub bd_avg_cold_warm_ratio: f64,
pub br_faster_cold_count: usize,
pub br_faster_warm_count: usize,
pub total_commands: usize,
}
struct RunResult {
duration: Duration,
success: bool,
#[allow(dead_code)]
stdout: Vec<u8>,
}
struct CapturedCommandRun {
duration: Duration,
exit_code: i32,
success: bool,
stdout: Vec<u8>,
stderr: Vec<u8>,
}
fn run_command(binary_path: &Path, args: &[&str], cwd: &Path) -> RunResult {
let start = Instant::now();
let output = Command::new(binary_path)
.args(args)
.current_dir(cwd)
.env("NO_COLOR", "1")
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.expect("Failed to run command");
let duration = start.elapsed();
RunResult {
duration,
success: output.status.success(),
stdout: output.stdout,
}
}
fn run_startup_matrix_command(
binary_path: &Path,
args: &[&str],
cwd: &Path,
env_vars: &[(&str, String)],
) -> std::io::Result<CapturedCommandRun> {
let mut command = Command::new(binary_path);
command.args(args).current_dir(cwd);
for key in [
"BD_ACTOR",
"BD_DB",
"BD_DATABASE",
"BEADS_DIR",
"BEADS_JSONL",
"BEADS_CACHE_DIR",
"BR_OUTPUT_FORMAT",
"TOON_DEFAULT_FORMAT",
"TOON_STATS",
] {
command.env_remove(key);
}
command.env("NO_COLOR", "1");
command.env("RUST_BACKTRACE", "1");
command.env("HOME", cwd);
for (key, value) in env_vars {
command.env(key, value);
}
let start = Instant::now();
let output = command
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()?;
Ok(CapturedCommandRun {
duration: start.elapsed(),
exit_code: output.status.code().unwrap_or(-1),
success: output.status.success(),
stdout: output.stdout,
stderr: output.stderr,
})
}
fn measure_cold_warm(
binary_path: &Path,
args: &[&str],
cwd: &Path,
binary_name: &str,
command_label: &str,
warm_runs: usize,
) -> ColdWarmMetrics {
let cold_result = run_command(binary_path, args, cwd);
let cold_start_ms = cold_result.duration.as_millis();
let mut warm_runs_ms = Vec::with_capacity(warm_runs);
let mut all_success = cold_result.success;
for _ in 0..warm_runs {
let result = run_command(binary_path, args, cwd);
warm_runs_ms.push(result.duration.as_millis());
all_success = all_success && result.success;
}
let warm_avg_ms = if warm_runs_ms.is_empty() {
0.0
} else {
warm_runs_ms.iter().sum::<u128>() as f64 / warm_runs_ms.len() as f64
};
let warm_std_dev_ms = if warm_runs_ms.len() < 2 {
0.0
} else {
let variance = warm_runs_ms
.iter()
.map(|&x| (x as f64 - warm_avg_ms).powi(2))
.sum::<f64>()
/ warm_runs_ms.len() as f64;
variance.sqrt()
};
let cold_warm_ratio = if warm_avg_ms > 0.0 {
cold_start_ms as f64 / warm_avg_ms
} else {
1.0
};
ColdWarmMetrics {
command: command_label.to_string(),
binary: binary_name.to_string(),
cold_start_ms,
warm_runs_ms,
warm_avg_ms,
cold_warm_ratio,
warm_std_dev_ms,
success: all_success,
}
}
fn create_br_workspace(br_path: &Path, issue_count: usize) -> std::io::Result<(TempDir, PathBuf)> {
let temp_dir = TempDir::new()?;
let root = temp_dir.path().to_path_buf();
fs::create_dir_all(root.join(".git"))?;
fs::write(root.join(".git").join("HEAD"), "ref: refs/heads/main\n")?;
let init_output = Command::new(br_path)
.args(["init"])
.current_dir(&root)
.output()?;
if !init_output.status.success() {
return Err(std::io::Error::other(format!(
"br init failed: {}",
String::from_utf8_lossy(&init_output.stderr)
)));
}
for i in 0..issue_count {
let title = format!("Benchmark issue {i}");
let priority = (i % 5).to_string();
let _ = Command::new(br_path)
.args(["create", "--title", &title, "--priority", &priority])
.current_dir(&root)
.output()?;
}
let _ = Command::new(br_path)
.args(["sync", "--flush-only"])
.current_dir(&root)
.output()?;
Ok((temp_dir, root))
}
fn copy_workspace_for_bd(br_root: &Path, bd_path: &Path) -> std::io::Result<(TempDir, PathBuf)> {
let temp_dir = TempDir::new()?;
let root = temp_dir.path().to_path_buf();
copy_dir_all(br_root, &root)?;
let br_db = root.join(".beads").join("beads.db");
if br_db.exists() {
fs::remove_file(&br_db)?;
}
let _ = fs::remove_file(root.join(".beads").join("beads.db-wal"));
let _ = fs::remove_file(root.join(".beads").join("beads.db-shm"));
let _ = fs::remove_file(root.join(".beads").join("beads.db-journal"));
let import_output = Command::new(bd_path)
.args(["sync", "--import-only"])
.current_dir(&root)
.output()?;
if !import_output.status.success() {
return Err(std::io::Error::other(format!(
"bd sync import failed: {}",
String::from_utf8_lossy(&import_output.stderr)
)));
}
Ok((temp_dir, root))
}
fn copy_dir_all(src: &Path, dst: &Path) -> std::io::Result<()> {
fs::create_dir_all(dst)?;
for entry in fs::read_dir(src)? {
let entry = entry?;
let path = entry.path();
let dst_path = dst.join(entry.file_name());
if path.is_dir() {
copy_dir_all(&path, &dst_path)?;
} else {
fs::copy(&path, &dst_path)?;
}
}
Ok(())
}
fn get_first_issue_id(br_path: &Path, workspace: &Path) -> Option<String> {
let output = Command::new(br_path)
.args(["list", "--limit=1", "--json"])
.current_dir(workspace)
.output()
.ok()?;
let stdout = String::from_utf8_lossy(&output.stdout);
for line in stdout.lines() {
if let Ok(issues) = serde_json::from_str::<Vec<serde_json::Value>>(line)
&& let Some(first) = issues.first()
&& let Some(id) = first.get("id").and_then(|v| v.as_str())
{
return Some(id.to_string());
}
}
None
}
const BENCHMARK_COMMANDS: &[(&str, &[&str])] = &[
("list", &["list", "--json"]),
("ready", &["ready", "--json"]),
("stats", &["stats", "--json"]),
("sync_status", &["sync", "--status"]),
];
const STARTUP_MATRIX_STATES: &[&str] = &[
"clean",
"stale",
"routed",
"no_db",
"read_only_fast_open",
"sync_status",
"recovery_anomaly",
];
const WARM_RUNS: usize = 5;
fn startup_matrix_args(state: &str) -> &'static [&'static str] {
match state {
"no_db" => &["--no-db", "list", "--json"],
"read_only_fast_open" => &["--no-auto-import", "--no-auto-flush", "list", "--json"],
"stale" | "sync_status" | "recovery_anomaly" => &["sync", "--status", "--json"],
_ => &["list", "--json"],
}
}
fn prepare_startup_matrix_workspace(
br_path: &Path,
state: &str,
) -> std::io::Result<(TempDir, PathBuf)> {
let (temp_dir, root) = create_br_workspace(br_path, 3)?;
match state {
"stale" => {
let output = Command::new(br_path)
.args(["create", "Startup matrix stale marker", "--no-auto-flush"])
.current_dir(&root)
.output()?;
if !output.status.success() {
return Err(std::io::Error::other(format!(
"startup matrix stale setup failed: {}",
String::from_utf8_lossy(&output.stderr)
)));
}
}
"recovery_anomaly" => {
let recovery_dir = root.join(".beads").join(".br_recovery");
fs::create_dir_all(&recovery_dir)?;
fs::write(
recovery_dir.join("startup-matrix-leftover.txt"),
"synthetic recovery artifact for startup matrix smoke\n",
)?;
}
_ => {}
}
Ok((temp_dir, root))
}
fn write_startup_matrix_state_artifacts(
bundle_dir: &Path,
state: &str,
args: &[&str],
cwd: &Path,
env_vars: &[(&str, String)],
run: &CapturedCommandRun,
) -> std::io::Result<StartupMatrixState> {
for subdir in ["logs", "timing", "syscalls", "rss", "raw"] {
fs::create_dir_all(bundle_dir.join(subdir))?;
}
let command_log_path = format!("logs/{state}.log");
let timing_summary_path = format!("timing/{state}.json");
let syscall_summary_path = format!("syscalls/{state}.json");
let rss_summary_path = format!("rss/{state}.json");
let stdout_path = format!("raw/{state}.stdout");
let stderr_path = format!("raw/{state}.stderr");
let env_keys = env_vars.iter().map(|(key, _)| *key).collect::<Vec<_>>();
let duration_ms = run.duration.as_secs_f64() * 1000.0;
let command_log = format!(
"state: {state}\nargs: {args:?}\ncwd: {}\nenv_keys: {env_keys:?}\nexit_code: {}\nsuccess: {}\nduration_ms: {duration_ms:.3}\nstdout_len: {}\nstderr_len: {}\n",
cwd.display(),
run.exit_code,
run.success,
run.stdout.len(),
run.stderr.len()
);
fs::write(bundle_dir.join(&command_log_path), command_log)?;
fs::write(bundle_dir.join(&stdout_path), &run.stdout)?;
fs::write(bundle_dir.join(&stderr_path), &run.stderr)?;
let timing_summary = serde_json::json!({
"state": state,
"args": args,
"cwd": cwd.display().to_string(),
"env_keys": env_keys,
"duration_ms": duration_ms,
"exit_code": run.exit_code,
"success": run.success,
"stdout_bytes": run.stdout.len(),
"stderr_bytes": run.stderr.len(),
});
fs::write(
bundle_dir.join(&timing_summary_path),
serde_json::to_string_pretty(&timing_summary)?,
)?;
let syscall_summary = serde_json::json!({
"state": state,
"collector": "startup_matrix_smoke",
"status": "not_collected",
"reason": "smoke runner records the required artifact slot without requiring strace or elevated privileges",
});
fs::write(
bundle_dir.join(&syscall_summary_path),
serde_json::to_string_pretty(&syscall_summary)?,
)?;
let rss_summary = serde_json::json!({
"state": state,
"collector": "startup_matrix_smoke",
"status": "not_collected",
"reason": "smoke runner records the required artifact slot; full matrix runners can replace this with platform RSS capture",
});
fs::write(
bundle_dir.join(&rss_summary_path),
serde_json::to_string_pretty(&rss_summary)?,
)?;
Ok(StartupMatrixState {
state: state.to_string(),
command_log_path,
timing_summary_path,
syscall_summary_path,
rss_summary_path,
raw_artifact_paths: vec![stdout_path, stderr_path],
})
}
fn write_startup_matrix_smoke_bundle(
br_path: &Path,
bundle_dir: &Path,
) -> std::io::Result<StartupMatrixManifest> {
fs::create_dir_all(bundle_dir)?;
let mut states = Vec::with_capacity(STARTUP_MATRIX_STATES.len());
for &state in STARTUP_MATRIX_STATES {
let (_workspace_guard, workspace_root) = prepare_startup_matrix_workspace(br_path, state)?;
let routed_cwd = if state == "routed" {
Some(TempDir::new()?)
} else {
None
};
let cwd = routed_cwd
.as_ref()
.map_or(workspace_root.as_path(), tempfile::TempDir::path);
let env_vars = if state == "routed" {
vec![(
"BEADS_DIR",
workspace_root.join(".beads").display().to_string(),
)]
} else {
Vec::new()
};
let args = startup_matrix_args(state);
let run = run_startup_matrix_command(br_path, args, cwd, &env_vars)?;
if !run.success {
return Err(std::io::Error::other(format!(
"startup matrix state {state} failed with code {}; stdout={}; stderr={}",
run.exit_code,
String::from_utf8_lossy(&run.stdout),
String::from_utf8_lossy(&run.stderr)
)));
}
states.push(write_startup_matrix_state_artifacts(
bundle_dir, state, args, cwd, &env_vars, &run,
)?);
}
let manifest = StartupMatrixManifest {
schema_version: "br.startup-matrix.v1".to_string(),
matrix_name: "storage-open-smoke".to_string(),
generated_at: chrono::Utc::now().to_rfc3339(),
states,
aggregation: StartupMatrixAggregation {
status: "ok".to_string(),
raw_evidence_preserved: true,
error: None,
},
};
fs::write(
bundle_dir.join("startup-matrix-manifest.json"),
serde_json::to_string_pretty(&manifest)?,
)?;
Ok(manifest)
}
fn sha256_hex(bytes: &[u8]) -> String {
hex_encode(&Sha256::digest(bytes))
}
fn git_revision_for_perf_evidence() -> String {
Command::new("git")
.args(["rev-parse", "--verify", "HEAD"])
.output()
.ok()
.filter(|output| output.status.success())
.map(|output| String::from_utf8_lossy(&output.stdout).trim().to_string())
.filter(|revision| !revision.is_empty())
.unwrap_or_else(|| "unknown".to_string())
}
fn git_dirty_for_perf_evidence() -> bool {
Command::new("git")
.args(["status", "--short"])
.output()
.ok()
.filter(|output| output.status.success())
.is_some_and(|output| !String::from_utf8_lossy(&output.stdout).trim().is_empty())
}
fn rustc_version_for_perf_evidence() -> Option<String> {
Command::new("rustc")
.arg("--version")
.output()
.ok()
.filter(|output| output.status.success())
.map(|output| String::from_utf8_lossy(&output.stdout).trim().to_string())
.filter(|version| !version.is_empty())
}
fn percentile(sorted_samples: &[f64], numerator: usize, denominator: usize) -> f64 {
if sorted_samples.is_empty() {
return 0.0;
}
let max_index = sorted_samples.len() - 1;
let index = max_index.saturating_mul(numerator).div_ceil(denominator);
sorted_samples[index.min(max_index)]
}
fn prepare_perf_evidence_bundle_dir(bundle_dir: &Path) -> std::io::Result<()> {
fs::create_dir_all(bundle_dir)?;
for subdir in [
"logs", "timing", "syscalls", "io", "rss", "golden", "proof", "baseline", "raw",
] {
fs::create_dir_all(bundle_dir.join(subdir))?;
}
Ok(())
}
fn record_perf_evidence_runs(
br_path: &Path,
workspace_root: &Path,
bundle_dir: &Path,
args: &[&str],
) -> std::io::Result<(Vec<CapturedCommandRun>, Vec<String>)> {
let mut runs = Vec::new();
let mut raw_artifact_paths = Vec::new();
for run_index in 0..3 {
let run = run_startup_matrix_command(br_path, args, workspace_root, &[])?;
if !run.success {
return Err(std::io::Error::other(format!(
"perf evidence smoke command failed with code {}; stdout={}; stderr={}",
run.exit_code,
String::from_utf8_lossy(&run.stdout),
String::from_utf8_lossy(&run.stderr)
)));
}
let stdout_path = format!("raw/list-{run_index}.stdout");
let stderr_path = format!("raw/list-{run_index}.stderr");
fs::write(bundle_dir.join(&stdout_path), &run.stdout)?;
fs::write(bundle_dir.join(&stderr_path), &run.stderr)?;
raw_artifact_paths.push(stdout_path);
raw_artifact_paths.push(stderr_path);
runs.push(run);
}
Ok((runs, raw_artifact_paths))
}
fn write_perf_evidence_golden(
bundle_dir: &Path,
runs: &[CapturedCommandRun],
) -> std::io::Result<(String, String)> {
let Some(first_run) = runs.first() else {
return Err(std::io::Error::other(
"perf evidence smoke requires at least one command run",
));
};
let stdout_sha256 = sha256_hex(&first_run.stdout);
let stderr_sha256 = sha256_hex(&first_run.stderr);
fs::write(bundle_dir.join("golden/stdout"), &first_run.stdout)?;
fs::write(bundle_dir.join("golden/stderr"), &first_run.stderr)?;
fs::write(
bundle_dir.join("golden/checksums.txt"),
format!("{stdout_sha256} golden/stdout\n{stderr_sha256} golden/stderr\n"),
)?;
Ok((stdout_sha256, stderr_sha256))
}
fn write_perf_evidence_timing(
bundle_dir: &Path,
runs: &[CapturedCommandRun],
) -> std::io::Result<PerfEvidenceTiming> {
let mut durations_ms = runs
.iter()
.map(|run| run.duration.as_secs_f64() * 1000.0)
.collect::<Vec<_>>();
durations_ms.sort_by(f64::total_cmp);
let timing = PerfEvidenceTiming {
sample_count: runs.len(),
min_ms: *durations_ms.first().unwrap_or(&0.0),
p50_ms: percentile(&durations_ms, 50, 100),
p95_ms: percentile(&durations_ms, 95, 100),
p99_ms: percentile(&durations_ms, 99, 100),
max_ms: *durations_ms.last().unwrap_or(&0.0),
summary_path: "timing/list.json".to_string(),
raw_samples_path: "timing/list-samples.jsonl".to_string(),
};
let sample_lines = runs
.iter()
.enumerate()
.map(|(run_index, run)| {
serde_json::json!({
"run_index": run_index,
"duration_ms": run.duration.as_secs_f64() * 1000.0,
"exit_code": run.exit_code,
"stdout_sha256": sha256_hex(&run.stdout),
"stderr_sha256": sha256_hex(&run.stderr),
})
.to_string()
})
.collect::<Vec<_>>()
.join("\n");
fs::write(
bundle_dir.join(&timing.raw_samples_path),
format!("{sample_lines}\n"),
)?;
fs::write(
bundle_dir.join(&timing.summary_path),
serde_json::to_string_pretty(&serde_json::json!({
"sample_count": timing.sample_count,
"min_ms": timing.min_ms,
"p50_ms": timing.p50_ms,
"p95_ms": timing.p95_ms,
"p99_ms": timing.p99_ms,
"max_ms": timing.max_ms,
}))?,
)?;
Ok(timing)
}
fn write_perf_evidence_support_artifacts(
bundle_dir: &Path,
workspace_root: &Path,
args: &[&str],
sample_count: usize,
stdout_sha256: &str,
stderr_sha256: &str,
) -> std::io::Result<()> {
fs::write(
bundle_dir.join("logs/list.log"),
format!(
"command: br {}\nworkspace: {}\nsamples: {}\nstdout_sha256: {stdout_sha256}\nstderr_sha256: {stderr_sha256}\n",
args.join(" "),
workspace_root.display(),
sample_count
),
)?;
for (path, collector) in [
("syscalls/list.json", "syscall"),
("io/list.json", "io"),
("rss/list.json", "rss"),
] {
fs::write(
bundle_dir.join(path),
serde_json::to_string_pretty(&serde_json::json!({
"collector": collector,
"status": "not_collected",
"reason": "smoke evidence bundle records the required slot; full release gates can replace this with platform capture",
}))?,
)?;
}
fs::write(
bundle_dir.join("proof/isomorphism.md"),
"## Change: perf evidence smoke ledger\n- Ordering preserved: yes; command output is not transformed.\n- Tie-breaking unchanged: yes; br list decides ordering.\n- Floating-point: N/A for command output; timings are evidence only.\n- RNG seeds: unchanged/N/A.\n- Golden outputs: stdout and stderr SHA-256 hashes recorded in golden/checksums.txt.\n",
)?;
Ok(())
}
fn build_perf_evidence_manifest(
br_path: &Path,
workspace_root: &Path,
args: &[&str],
timing: PerfEvidenceTiming,
hashes: (String, String),
raw_artifact_paths: Vec<String>,
) -> std::io::Result<PerfEvidenceManifest> {
let (stdout_sha256, stderr_sha256) = hashes;
let issues_jsonl = fs::read(workspace_root.join(".beads").join("issues.jsonl"))?;
let generated_at = chrono::Utc::now();
Ok(PerfEvidenceManifest {
schema_version: "br.perf-evidence.v1".to_string(),
generated_at: generated_at.to_rfc3339(),
valid_until: Some((generated_at + chrono::Duration::days(30)).to_rfc3339()),
command: PerfEvidenceCommand {
label: "list_json".to_string(),
args: args.iter().map(|arg| (*arg).to_string()).collect(),
},
dataset: PerfEvidenceDataset {
name: "tiny-smoke".to_string(),
issue_count: Some(3),
content_hash: Some(sha256_hex(&issues_jsonl)),
},
git: PerfEvidenceGit {
revision: git_revision_for_perf_evidence(),
dirty: git_dirty_for_perf_evidence(),
},
binary: PerfEvidenceBinary {
path: br_path.display().to_string(),
version: None,
},
environment: PerfEvidenceEnvironment {
os: std::env::consts::OS.to_string(),
rustc: rustc_version_for_perf_evidence(),
env: vec![PerfEvidenceEnvVar {
name: "NO_COLOR".to_string(),
value_hash: Some(sha256_hex(b"1")),
}],
},
timing,
resources: PerfEvidenceResources {
syscalls: "syscalls/list.json".to_string(),
io: "io/list.json".to_string(),
rss: "rss/list.json".to_string(),
},
golden: PerfEvidenceGolden {
stdout_sha256,
stderr_sha256: Some(stderr_sha256),
checksums_path: "golden/checksums.txt".to_string(),
stdout_path: "golden/stdout".to_string(),
stderr_path: Some("golden/stderr".to_string()),
},
isomorphism_note_path: "proof/isomorphism.md".to_string(),
policy: PerfEvidencePolicy {
mode: "enforcing".to_string(),
baseline_manifest_path: Some("baseline/perf-evidence-manifest.json".to_string()),
latency_regression_budget_pct: Some(5.0),
syscall_regression_budget_pct: Some(10.0),
output_hash_must_match: true,
},
comparison: PerfEvidenceComparison {
status: "pass".to_string(),
baseline_manifest_path: Some("baseline/perf-evidence-manifest.json".to_string()),
p95_delta_pct: Some(0.0),
stdout_hash_match: Some(true),
syscall_delta_pct: Some(0.0),
decision_reason: "self-baseline smoke comparison passed enforcing policy".to_string(),
},
raw_artifact_paths,
})
}
fn write_perf_evidence_smoke_bundle(
br_path: &Path,
bundle_dir: &Path,
) -> std::io::Result<PerfEvidenceManifest> {
prepare_perf_evidence_bundle_dir(bundle_dir)?;
let (_workspace_guard, workspace_root) = create_br_workspace(br_path, 3)?;
let args = ["list", "--json"];
let (runs, raw_artifact_paths) =
record_perf_evidence_runs(br_path, &workspace_root, bundle_dir, &args)?;
let hashes = write_perf_evidence_golden(bundle_dir, &runs)?;
let timing = write_perf_evidence_timing(bundle_dir, &runs)?;
write_perf_evidence_support_artifacts(
bundle_dir,
&workspace_root,
&args,
runs.len(),
&hashes.0,
&hashes.1,
)?;
let manifest = build_perf_evidence_manifest(
br_path,
&workspace_root,
&args,
timing,
hashes,
raw_artifact_paths,
)?;
let manifest_json = serde_json::to_string_pretty(&manifest)?;
fs::write(
bundle_dir.join("perf-evidence-manifest.json"),
&manifest_json,
)?;
fs::write(
bundle_dir.join("baseline/perf-evidence-manifest.json"),
manifest_json,
)?;
Ok(manifest)
}
fn benchmark_cold_warm(
binaries: &DiscoveredBinaries,
issue_count: usize,
) -> Result<ColdWarmBenchmark, String> {
let bd = binaries.require_bd()?;
eprintln!("Setting up workspace with {issue_count} issues...");
let (_br_temp, br_root) = create_br_workspace(&binaries.br.path, issue_count)
.map_err(|e| format!("Failed to create br workspace: {e}"))?;
let (_bd_temp, bd_root) = copy_workspace_for_bd(&br_root, &bd.path)
.map_err(|e| format!("Failed to create bd workspace: {e}"))?;
let issue_id = get_first_issue_id(&binaries.br.path, &br_root);
let mut comparisons = Vec::new();
for (label, args) in BENCHMARK_COMMANDS {
eprintln!(" Benchmarking {label}...");
let br_metrics =
measure_cold_warm(&binaries.br.path, args, &br_root, "br", label, WARM_RUNS);
let bd_metrics = measure_cold_warm(&bd.path, args, &bd_root, "bd", label, WARM_RUNS);
let cold_ratio_br_bd = if bd_metrics.cold_start_ms > 0 {
br_metrics.cold_start_ms as f64 / bd_metrics.cold_start_ms as f64
} else {
1.0
};
let warm_ratio_br_bd = if bd_metrics.warm_avg_ms > 0.0 {
br_metrics.warm_avg_ms / bd_metrics.warm_avg_ms
} else {
1.0
};
comparisons.push(ColdWarmComparison {
command: label.to_string(),
br: br_metrics,
bd: bd_metrics,
cold_ratio_br_bd,
warm_ratio_br_bd,
});
}
if let Some(id) = issue_id {
eprintln!(" Benchmarking show...");
let show_args: Vec<&str> = vec!["show", &id, "--json"];
let br_metrics = measure_cold_warm(
&binaries.br.path,
&show_args,
&br_root,
"br",
"show",
WARM_RUNS,
);
let bd_metrics = measure_cold_warm(&bd.path, &show_args, &bd_root, "bd", "show", WARM_RUNS);
let cold_ratio_br_bd = if bd_metrics.cold_start_ms > 0 {
br_metrics.cold_start_ms as f64 / bd_metrics.cold_start_ms as f64
} else {
1.0
};
let warm_ratio_br_bd = if bd_metrics.warm_avg_ms > 0.0 {
br_metrics.warm_avg_ms / bd_metrics.warm_avg_ms
} else {
1.0
};
comparisons.push(ColdWarmComparison {
command: "show".to_string(),
br: br_metrics,
bd: bd_metrics,
cold_ratio_br_bd,
warm_ratio_br_bd,
});
}
let br_cold_warm_ratios: Vec<f64> = comparisons.iter().map(|c| c.br.cold_warm_ratio).collect();
let bd_cold_warm_ratios: Vec<f64> = comparisons.iter().map(|c| c.bd.cold_warm_ratio).collect();
let br_avg_cold_warm_ratio = if br_cold_warm_ratios.is_empty() {
1.0
} else {
br_cold_warm_ratios.iter().sum::<f64>() / br_cold_warm_ratios.len() as f64
};
let bd_avg_cold_warm_ratio = if bd_cold_warm_ratios.is_empty() {
1.0
} else {
bd_cold_warm_ratios.iter().sum::<f64>() / bd_cold_warm_ratios.len() as f64
};
let br_faster_cold_count = comparisons
.iter()
.filter(|c| c.cold_ratio_br_bd < 1.0)
.count();
let br_faster_warm_count = comparisons
.iter()
.filter(|c| c.warm_ratio_br_bd < 1.0)
.count();
let summary = ColdWarmSummary {
br_avg_cold_warm_ratio,
bd_avg_cold_warm_ratio,
br_faster_cold_count,
br_faster_warm_count,
total_commands: comparisons.len(),
};
let timestamp = chrono::Utc::now().to_rfc3339();
Ok(ColdWarmBenchmark {
dataset_name: format!("synthetic_{issue_count}"),
issue_count,
comparisons,
summary,
timestamp,
})
}
fn print_benchmark(benchmark: &ColdWarmBenchmark) {
let sep = "=".repeat(100);
let dash = "-".repeat(100);
println!("\n{sep}");
println!(
"Cold vs Warm Start Benchmark: {} ({} issues)",
benchmark.dataset_name, benchmark.issue_count
);
println!("{sep}");
println!(
"\n{:<15} {:>12} {:>12} {:>10} {:>12} {:>12} {:>10} {:>12} {:>12}",
"Command",
"br Cold(ms)",
"br Warm(ms)",
"br C/W",
"bd Cold(ms)",
"bd Warm(ms)",
"bd C/W",
"Cold br/bd",
"Warm br/bd"
);
println!("{dash}");
for c in &benchmark.comparisons {
println!(
"{:<15} {:>12} {:>12.1} {:>10.2}x {:>12} {:>12.1} {:>10.2}x {:>12.2}x {:>12.2}x",
c.command,
c.br.cold_start_ms,
c.br.warm_avg_ms,
c.br.cold_warm_ratio,
c.bd.cold_start_ms,
c.bd.warm_avg_ms,
c.bd.cold_warm_ratio,
c.cold_ratio_br_bd,
c.warm_ratio_br_bd
);
}
println!("{dash}");
println!("\nSummary:");
println!(
" br average cold/warm ratio: {:.2}x",
benchmark.summary.br_avg_cold_warm_ratio
);
println!(
" bd average cold/warm ratio: {:.2}x",
benchmark.summary.bd_avg_cold_warm_ratio
);
println!(
" br faster on cold start: {}/{} commands",
benchmark.summary.br_faster_cold_count, benchmark.summary.total_commands
);
println!(
" br faster on warm start: {}/{} commands",
benchmark.summary.br_faster_warm_count, benchmark.summary.total_commands
);
println!();
}
fn write_results_json(benchmarks: &[ColdWarmBenchmark], output_path: &Path) -> std::io::Result<()> {
let file = File::create(output_path)?;
let writer = BufWriter::new(file);
serde_json::to_writer_pretty(writer, benchmarks)?;
Ok(())
}
#[test]
#[ignore = "manual benchmark: cargo test --test bench_cold_warm_start -- --ignored --nocapture"]
fn cold_warm_small() {
println!("\n=== Cold vs Warm Start Benchmark: Small (50 issues) ===\n");
let binaries = match discover_binaries() {
Ok(b) => b,
Err(e) => {
eprintln!("Binary discovery failed: {e}");
panic!("Cannot run benchmarks without br binary");
}
};
if binaries.bd.is_none() {
println!("bd not found, skipping benchmark");
return;
}
match benchmark_cold_warm(&binaries, 50) {
Ok(benchmark) => {
print_benchmark(&benchmark);
let output_dir = PathBuf::from("target/benchmark-results");
fs::create_dir_all(&output_dir).expect("create output dir");
let output_path = output_dir.join("cold_warm_small_latest.json");
write_results_json(&[benchmark], &output_path).expect("write results");
println!("Results written to: {}", output_path.display());
}
Err(e) => {
eprintln!("Benchmark failed: {e}");
}
}
}
#[test]
#[ignore = "manual benchmark: cargo test --test bench_cold_warm_start -- --ignored --nocapture"]
fn cold_warm_medium() {
println!("\n=== Cold vs Warm Start Benchmark: Medium (200 issues) ===\n");
let binaries = match discover_binaries() {
Ok(b) => b,
Err(e) => {
eprintln!("Binary discovery failed: {e}");
panic!("Cannot run benchmarks without br binary");
}
};
if binaries.bd.is_none() {
println!("bd not found, skipping benchmark");
return;
}
match benchmark_cold_warm(&binaries, 200) {
Ok(benchmark) => {
print_benchmark(&benchmark);
let output_dir = PathBuf::from("target/benchmark-results");
fs::create_dir_all(&output_dir).expect("create output dir");
let output_path = output_dir.join("cold_warm_medium_latest.json");
write_results_json(&[benchmark], &output_path).expect("write results");
println!("Results written to: {}", output_path.display());
}
Err(e) => {
eprintln!("Benchmark failed: {e}");
}
}
}
#[test]
#[ignore = "manual benchmark: cargo test --test bench_cold_warm_start -- --ignored --nocapture"]
fn cold_warm_large() {
println!("\n=== Cold vs Warm Start Benchmark: Large (500 issues) ===\n");
let binaries = match discover_binaries() {
Ok(b) => b,
Err(e) => {
eprintln!("Binary discovery failed: {e}");
panic!("Cannot run benchmarks without br binary");
}
};
if binaries.bd.is_none() {
println!("bd not found, skipping benchmark");
return;
}
match benchmark_cold_warm(&binaries, 500) {
Ok(benchmark) => {
print_benchmark(&benchmark);
let output_dir = PathBuf::from("target/benchmark-results");
fs::create_dir_all(&output_dir).expect("create output dir");
let output_path = output_dir.join("cold_warm_large_latest.json");
write_results_json(&[benchmark], &output_path).expect("write results");
println!("Results written to: {}", output_path.display());
}
Err(e) => {
eprintln!("Benchmark failed: {e}");
}
}
}
#[test]
#[ignore = "manual benchmark: cargo test --test bench_cold_warm_start cold_warm_all -- --ignored --nocapture"]
fn cold_warm_all() {
println!("\n=== Cold vs Warm Start Benchmark Suite ===\n");
let binaries = match discover_binaries() {
Ok(b) => b,
Err(e) => {
eprintln!("Binary discovery failed: {e}");
panic!("Cannot run benchmarks without br binary");
}
};
println!(
"br: {} ({})",
binaries.br.path.display(),
binaries.br.version
);
if let Some(ref bd) = binaries.bd {
println!("bd: {} ({})", bd.path.display(), bd.version);
} else {
println!("bd: NOT FOUND - skipping benchmarks");
return;
}
let mut all_benchmarks = Vec::new();
for &issue_count in &[50, 200, 500] {
println!("\n--- Testing with {issue_count} issues ---");
match benchmark_cold_warm(&binaries, issue_count) {
Ok(benchmark) => {
print_benchmark(&benchmark);
all_benchmarks.push(benchmark);
}
Err(e) => {
eprintln!("Benchmark failed for {issue_count} issues: {e}");
}
}
}
if !all_benchmarks.is_empty() {
let output_dir = PathBuf::from("target/benchmark-results");
fs::create_dir_all(&output_dir).expect("create output dir");
let timestamp = chrono::Utc::now().format("%Y%m%d_%H%M%S");
let output_path = output_dir.join(format!("cold_warm_all_{timestamp}.json"));
write_results_json(&all_benchmarks, &output_path).expect("write results");
println!("\nAll results written to: {}", output_path.display());
let latest_path = output_dir.join("cold_warm_all_latest.json");
write_results_json(&all_benchmarks, &latest_path).expect("write latest");
}
println!("\n{}", "=".repeat(100));
println!("OVERALL SUMMARY");
println!("{}", "=".repeat(100));
for b in &all_benchmarks {
println!("\n{}: {} issues", b.dataset_name, b.issue_count);
println!(
" br cold/warm ratio: {:.2}x, bd cold/warm ratio: {:.2}x",
b.summary.br_avg_cold_warm_ratio, b.summary.bd_avg_cold_warm_ratio
);
println!(
" br faster: cold {}/{}, warm {}/{}",
b.summary.br_faster_cold_count,
b.summary.total_commands,
b.summary.br_faster_warm_count,
b.summary.total_commands
);
}
}
#[test]
#[ignore = "manual benchmark: cargo test --test bench_cold_warm_start cold_warm_real_datasets -- --ignored --nocapture"]
#[allow(clippy::too_many_lines)]
fn cold_warm_real_datasets() {
println!("\n=== Cold vs Warm Start Benchmark: Real Datasets ===\n");
let binaries = match discover_binaries() {
Ok(b) => b,
Err(e) => {
eprintln!("Binary discovery failed: {e}");
panic!("Cannot run benchmarks without br binary");
}
};
let bd = match binaries.require_bd() {
Ok(b) => b,
Err(e) => {
eprintln!("bd not found: {e}");
return;
}
};
println!(
"br: {} ({})",
binaries.br.path.display(),
binaries.br.version
);
println!("bd: {} ({})", bd.path.display(), bd.version);
let mut all_results = Vec::new();
for dataset in KnownDataset::all() {
if !dataset.beads_dir().exists() {
println!("\nSkipping {} (not available)", dataset.name());
continue;
}
println!("\n--- Dataset: {} ---", dataset.name());
let br_isolated = match IsolatedDataset::from_dataset(*dataset) {
Ok(d) => d,
Err(e) => {
eprintln!("Failed to create br workspace: {e}");
continue;
}
};
let bd_isolated = match IsolatedDataset::from_dataset(*dataset) {
Ok(d) => d,
Err(e) => {
eprintln!("Failed to create bd workspace: {e}");
continue;
}
};
let issue_count = br_isolated.metadata.issue_count;
let issue_id = get_first_issue_id(&binaries.br.path, br_isolated.workspace_root());
let mut comparisons = Vec::new();
for (label, args) in BENCHMARK_COMMANDS {
eprintln!(" Benchmarking {label}...");
let br_metrics = measure_cold_warm(
&binaries.br.path,
args,
br_isolated.workspace_root(),
"br",
label,
WARM_RUNS,
);
let bd_metrics = measure_cold_warm(
&bd.path,
args,
bd_isolated.workspace_root(),
"bd",
label,
WARM_RUNS,
);
let cold_ratio_br_bd = if bd_metrics.cold_start_ms > 0 {
br_metrics.cold_start_ms as f64 / bd_metrics.cold_start_ms as f64
} else {
1.0
};
let warm_ratio_br_bd = if bd_metrics.warm_avg_ms > 0.0 {
br_metrics.warm_avg_ms / bd_metrics.warm_avg_ms
} else {
1.0
};
comparisons.push(ColdWarmComparison {
command: label.to_string(),
br: br_metrics,
bd: bd_metrics,
cold_ratio_br_bd,
warm_ratio_br_bd,
});
}
if let Some(id) = issue_id {
eprintln!(" Benchmarking show...");
let show_args: Vec<&str> = vec!["show", &id, "--json"];
let br_metrics = measure_cold_warm(
&binaries.br.path,
&show_args,
br_isolated.workspace_root(),
"br",
"show",
WARM_RUNS,
);
let bd_metrics = measure_cold_warm(
&bd.path,
&show_args,
bd_isolated.workspace_root(),
"bd",
"show",
WARM_RUNS,
);
let cold_ratio_br_bd = if bd_metrics.cold_start_ms > 0 {
br_metrics.cold_start_ms as f64 / bd_metrics.cold_start_ms as f64
} else {
1.0
};
let warm_ratio_br_bd = if bd_metrics.warm_avg_ms > 0.0 {
br_metrics.warm_avg_ms / bd_metrics.warm_avg_ms
} else {
1.0
};
comparisons.push(ColdWarmComparison {
command: "show".to_string(),
br: br_metrics,
bd: bd_metrics,
cold_ratio_br_bd,
warm_ratio_br_bd,
});
}
let br_cold_warm_ratios: Vec<f64> =
comparisons.iter().map(|c| c.br.cold_warm_ratio).collect();
let bd_cold_warm_ratios: Vec<f64> =
comparisons.iter().map(|c| c.bd.cold_warm_ratio).collect();
let br_avg_cold_warm_ratio = if br_cold_warm_ratios.is_empty() {
1.0
} else {
br_cold_warm_ratios.iter().sum::<f64>() / br_cold_warm_ratios.len() as f64
};
let bd_avg_cold_warm_ratio = if bd_cold_warm_ratios.is_empty() {
1.0
} else {
bd_cold_warm_ratios.iter().sum::<f64>() / bd_cold_warm_ratios.len() as f64
};
let summary = ColdWarmSummary {
br_avg_cold_warm_ratio,
bd_avg_cold_warm_ratio,
br_faster_cold_count: comparisons
.iter()
.filter(|c| c.cold_ratio_br_bd < 1.0)
.count(),
br_faster_warm_count: comparisons
.iter()
.filter(|c| c.warm_ratio_br_bd < 1.0)
.count(),
total_commands: comparisons.len(),
};
let benchmark = ColdWarmBenchmark {
dataset_name: dataset.name().to_string(),
issue_count,
comparisons,
summary,
timestamp: chrono::Utc::now().to_rfc3339(),
};
print_benchmark(&benchmark);
all_results.push(benchmark);
}
if !all_results.is_empty() {
let output_dir = PathBuf::from("target/benchmark-results");
fs::create_dir_all(&output_dir).expect("create output dir");
let output_path = output_dir.join("cold_warm_real_datasets_latest.json");
write_results_json(&all_results, &output_path).expect("write results");
println!("\nResults written to: {}", output_path.display());
}
}
#[test]
fn startup_matrix_smoke_bundle_covers_storage_open_states() -> std::io::Result<()> {
let br_path = assert_cmd::cargo::cargo_bin!("br");
let run_id = format!(
"startup-matrix-smoke-{}-{}",
chrono::Utc::now().format("%Y%m%dT%H%M%S%fZ"),
std::process::id()
);
let bundle_dir = PathBuf::from("target").join("perf-artifacts").join(run_id);
let manifest = write_startup_matrix_smoke_bundle(br_path, &bundle_dir)?;
let validation = ArtifactValidator::new().validate_startup_matrix_bundle_dir(&bundle_dir);
assert!(
validation.valid,
"startup matrix bundle should validate: {:?}",
validation.errors
);
let mut states = manifest
.states
.iter()
.map(|state| state.state.as_str())
.collect::<Vec<_>>();
states.sort_unstable();
let mut expected = STARTUP_MATRIX_STATES.to_vec();
expected.sort_unstable();
assert_eq!(states, expected);
Ok(())
}
#[test]
fn perf_evidence_smoke_bundle_records_list_json_command() -> std::io::Result<()> {
let br_path = assert_cmd::cargo::cargo_bin!("br");
let run_id = format!(
"perf-evidence-smoke-{}-{}",
chrono::Utc::now().format("%Y%m%dT%H%M%S%fZ"),
std::process::id()
);
let bundle_dir = PathBuf::from("target").join("perf-artifacts").join(run_id);
let manifest = write_perf_evidence_smoke_bundle(br_path, &bundle_dir)?;
let validation = ArtifactValidator::new().validate_perf_evidence_bundle_dir(&bundle_dir);
assert!(
validation.valid,
"perf evidence bundle should validate: {:?}",
validation.errors
);
assert_eq!(manifest.schema_version, "br.perf-evidence.v1");
assert_eq!(manifest.command.args, ["list", "--json"]);
assert_eq!(manifest.policy.mode, "enforcing");
assert_eq!(manifest.comparison.status, "pass");
assert_eq!(manifest.comparison.stdout_hash_match, Some(true));
assert_eq!(manifest.timing.sample_count, 3);
assert!(manifest.timing.p50_ms <= manifest.timing.p95_ms);
assert!(manifest.timing.p95_ms <= manifest.timing.p99_ms);
Ok(())
}
#[test]
fn test_cold_warm_ratio() {
let cold_start_ms: f64 = 100.0;
let warm_avg_ms: f64 = 50.0;
let ratio = cold_start_ms / warm_avg_ms;
assert!((ratio - 2.0).abs() < 0.01);
}
#[test]
fn test_std_dev_calculation() {
let warm_runs_ms = [10u128, 12, 11, 13, 11];
let warm_avg_ms = warm_runs_ms.iter().sum::<u128>() as f64 / warm_runs_ms.len() as f64;
let variance = warm_runs_ms
.iter()
.map(|&x| (x as f64 - warm_avg_ms).powi(2))
.sum::<f64>()
/ warm_runs_ms.len() as f64;
let std_dev = variance.sqrt();
assert!((warm_avg_ms - 11.4).abs() < 0.1);
assert!(std_dev > 0.9 && std_dev < 1.2);
}