use serde::{Deserialize, Serialize};
use crate::admission_rejection::{AdmissionRejectionSummary, RejectionClass};
use crate::capability_probe::CapabilityRequirement;
use crate::patterns::{
CompilationKind, classify_command, classify_command_detailed, is_cargo_xwin_build,
split_shell_commands,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AdmitRecommendation {
Offload,
Local,
Queue,
Defer,
}
impl AdmitRecommendation {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
AdmitRecommendation::Offload => "offload",
AdmitRecommendation::Local => "local",
AdmitRecommendation::Queue => "queue",
AdmitRecommendation::Defer => "defer",
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct RequiredCapabilities {
pub needs_cargo: bool,
pub needs_bun: bool,
pub needs_nix: bool,
pub needs_go: bool,
pub needs_node: bool,
pub needs_zig: bool,
pub needs_targets: Vec<String>,
pub needs_toolchains: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub needs_os: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub needs_tools: Vec<String>,
}
impl RequiredCapabilities {
#[must_use]
pub fn to_requirement(&self, min_protocol: u32) -> CapabilityRequirement {
CapabilityRequirement {
needs_targets: self.needs_targets.clone(),
min_protocol,
needs_cargo: self.needs_cargo,
needs_bun: self.needs_bun,
needs_nix: self.needs_nix,
needs_go: self.needs_go,
needs_node: self.needs_node,
needs_zig: self.needs_zig,
needs_toolchains: self.needs_toolchains.clone(),
needs_os: self.needs_os.clone(),
needs_tools: self.needs_tools.clone(),
..CapabilityRequirement::default()
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AdmitPreflight {
pub command: String,
pub compound: Vec<String>,
pub is_compilation: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub family: Option<String>,
pub required: RequiredCapabilities,
pub proof_policy: bool,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub job_mode: bool,
pub base_recommendation: AdmitRecommendation,
pub detail: String,
}
fn family_token(kind: CompilationKind) -> &'static str {
match kind {
CompilationKind::CargoBuild => "cargo_build",
CompilationKind::CargoTest => "cargo_test",
CompilationKind::CargoCheck => "cargo_check",
CompilationKind::CargoClippy => "cargo_clippy",
CompilationKind::CargoDoc => "cargo_doc",
CompilationKind::CargoNextest => "cargo_nextest",
CompilationKind::CargoBench => "cargo_bench",
CompilationKind::CargoZigbuild => "cargo_zigbuild",
CompilationKind::Rustc => "rustc",
CompilationKind::Gcc => "gcc",
CompilationKind::Gpp => "gpp",
CompilationKind::Clang => "clang",
CompilationKind::Clangpp => "clangpp",
CompilationKind::Make => "make",
CompilationKind::CmakeBuild => "cmake",
CompilationKind::Ninja => "ninja",
CompilationKind::Meson => "meson",
CompilationKind::BunTest => "bun_test",
CompilationKind::BunTypecheck => "bun_typecheck",
CompilationKind::NixBuild => "nix_build",
CompilationKind::GoBuild => "go_build",
CompilationKind::GoTest => "go_test",
CompilationKind::GoVet => "go_vet",
CompilationKind::Tsc => "tsc",
CompilationKind::Job => "job",
}
}
fn is_rust_kind(kind: CompilationKind) -> bool {
matches!(
kind,
CompilationKind::CargoBuild
| CompilationKind::CargoTest
| CompilationKind::CargoCheck
| CompilationKind::CargoClippy
| CompilationKind::CargoDoc
| CompilationKind::CargoNextest
| CompilationKind::CargoBench
| CompilationKind::CargoZigbuild
| CompilationKind::Rustc
)
}
fn derive_capabilities(command: &str, kind: Option<CompilationKind>) -> RequiredCapabilities {
let mut req = RequiredCapabilities::default();
if let Some(kind) = kind {
req.needs_cargo = is_rust_kind(kind);
req.needs_bun = matches!(
kind,
CompilationKind::BunTest | CompilationKind::BunTypecheck
);
req.needs_nix = matches!(kind, CompilationKind::NixBuild);
req.needs_go = matches!(
kind,
CompilationKind::GoBuild | CompilationKind::GoTest | CompilationKind::GoVet
);
req.needs_node = matches!(kind, CompilationKind::Tsc);
req.needs_zig = matches!(kind, CompilationKind::CargoZigbuild);
}
let tokens: Vec<&str> = command.split_whitespace().collect();
let mut i = 0;
while i < tokens.len() {
let tok = tokens[i];
if let Some(triple) = tok.strip_prefix("--target=") {
if !triple.is_empty() {
req.needs_targets.push(triple.to_string());
}
} else if tok == "--target" {
if let Some(triple) = tokens.get(i + 1) {
req.needs_targets.push((*triple).to_string());
i += 1;
}
} else if let Some(toolchain) = tok.strip_prefix('+') {
if !toolchain.is_empty() {
req.needs_toolchains.push(toolchain.to_string());
}
}
i += 1;
}
req.needs_targets.sort();
req.needs_targets.dedup();
req.needs_toolchains.sort();
req.needs_toolchains.dedup();
req.needs_os = os_from_target_triples(&host_bound_targets(command, &req.needs_targets));
req
}
fn host_bound_targets(command: &str, targets: &[String]) -> Vec<String> {
let cross_msvc = is_cargo_xwin_build(command);
let cross_darwin = classify_command(command).kind == Some(CompilationKind::CargoZigbuild);
targets
.iter()
.filter(|target| {
let target = target.to_ascii_lowercase();
!(cross_msvc && target.contains("-windows-msvc")
|| cross_darwin && target.ends_with("-apple-darwin"))
})
.cloned()
.collect()
}
#[must_use]
pub fn required_os_for_command(command: &str) -> Option<String> {
let mut triples: Vec<String> = Vec::new();
for part in split_shell_commands(command) {
let detail = classify_command_detailed(part);
if detail.classification.is_compilation {
let required = derive_capabilities(part, detail.classification.kind);
triples.extend(host_bound_targets(part, &required.needs_targets));
}
}
os_from_target_triples(&triples)
}
fn os_from_target_triples(triples: &[String]) -> Option<String> {
let mut required: Option<String> = None;
for triple in triples {
let lower = triple.to_ascii_lowercase();
let os = if lower.contains("-windows-msvc") {
"windows"
} else if lower.contains("-apple-") {
"darwin"
} else {
continue;
};
match &required {
Some(existing) if existing != os => return None,
Some(_) => {}
None => required = Some(os.to_string()),
}
}
required
}
#[must_use]
pub fn preflight(command: &str, proof_policy: bool) -> AdmitPreflight {
let compound: Vec<String> = split_shell_commands(command)
.into_iter()
.map(Into::into)
.collect();
let compound = if compound.is_empty() {
vec![command.to_string()]
} else {
compound
};
let mut is_compilation = false;
let mut family: Option<String> = None;
let mut required = RequiredCapabilities::default();
let mut host_targets = Vec::new();
for part in &compound {
let detail = classify_command_detailed(part);
let c = &detail.classification;
if c.is_compilation {
is_compilation = true;
if let Some(kind) = c.kind
&& family.is_none()
{
family = Some(family_token(kind).to_string());
}
let part_req = derive_capabilities(part, c.kind);
host_targets.extend(host_bound_targets(part, &part_req.needs_targets));
required.needs_cargo |= part_req.needs_cargo;
required.needs_zig |= part_req.needs_zig;
required.needs_bun |= part_req.needs_bun;
required.needs_nix |= part_req.needs_nix;
required.needs_go |= part_req.needs_go;
required.needs_node |= part_req.needs_node;
required.needs_targets.extend(part_req.needs_targets);
required.needs_toolchains.extend(part_req.needs_toolchains);
}
}
required.needs_targets.sort();
required.needs_targets.dedup();
required.needs_toolchains.sort();
required.needs_toolchains.dedup();
required.needs_os = os_from_target_triples(&host_targets);
let base_recommendation = if is_compilation {
AdmitRecommendation::Offload
} else {
AdmitRecommendation::Local
};
let detail = if !is_compilation {
"not a compilation command; run locally".to_string()
} else if compound.len() > 1 {
format!(
"compound command with {} parts; offload-eligible (family={})",
compound.len(),
family.as_deref().unwrap_or("unknown")
)
} else {
format!(
"offload-eligible (family={})",
family.as_deref().unwrap_or("unknown")
)
};
AdmitPreflight {
command: command.to_string(),
compound,
is_compilation,
family,
required,
proof_policy,
job_mode: false,
base_recommendation,
detail,
}
}
impl AdmitPreflight {
#[must_use]
pub fn as_job(mut self, required_tools: Vec<String>) -> Self {
self.job_mode = true;
self.required.needs_tools = required_tools;
self.required.needs_tools.sort();
self.required.needs_tools.dedup();
self.base_recommendation = AdmitRecommendation::Offload;
let tools = if self.required.needs_tools.is_empty() {
String::new()
} else {
format!(
"; requires verified tool(s) {}",
self.required.needs_tools.join(", ")
)
};
self.detail = format!(
"job-mode admission: classifier bypassed, offload-eligible (classified as {}{})",
if self.is_compilation {
self.family.as_deref().unwrap_or("a compilation")
} else {
"non-compilation"
},
tools
);
self
}
}
#[must_use]
pub fn refine_recommendation(
base: AdmitRecommendation,
summary: &AdmissionRejectionSummary,
) -> AdmitRecommendation {
if base != AdmitRecommendation::Offload {
return base;
}
if !summary.all_rejected() {
return AdmitRecommendation::Offload;
}
let command_specific = summary.class_count(RejectionClass::CommandAdmissibility)
+ summary.class_count(RejectionClass::ProjectPolicy);
let health = summary.class_count(RejectionClass::WorkerHealth);
if command_specific >= health {
AdmitRecommendation::Defer
} else {
AdmitRecommendation::Queue
}
}
#[must_use]
pub fn is_offloadable(command: &str) -> bool {
classify_command(command).is_compilation
}
#[cfg(test)]
mod tests {
use super::*;
use crate::admission_rejection::{
AdmissionRejectionCategory, CandidateRejection, aggregate_rejections,
};
#[test]
fn job_mode_preflight_is_offload_eligible_whatever_the_classifier_said() {
let script = preflight("./run_shards.sh --shard 3", false);
assert!(!script.is_compilation);
assert_eq!(script.base_recommendation, AdmitRecommendation::Local);
let job = script.clone().as_job(Vec::new());
assert!(job.job_mode);
assert_eq!(job.base_recommendation, AdmitRecommendation::Offload);
assert!(job.detail.contains("classifier bypassed"), "{}", job.detail);
assert!(!job.is_compilation);
assert_eq!(job.command, script.command);
assert_eq!(job.compound, script.compound);
let compile = preflight("cargo test --workspace", false).as_job(Vec::new());
assert!(compile.is_compilation);
assert_eq!(compile.family.as_deref(), Some("cargo_test"));
assert_eq!(compile.base_recommendation, AdmitRecommendation::Offload);
assert!(compile.detail.contains("cargo_test"), "{}", compile.detail);
}
#[test]
fn job_mode_required_tools_are_caller_stated_normalized_and_gate_ready() {
assert!(
preflight("./fuzz.sh", false)
.as_job(Vec::new())
.required
.needs_tools
.is_empty()
);
let job = preflight("./fuzz.sh", false).as_job(vec![
"clang".to_string(),
"ld.lld".to_string(),
"clang".to_string(),
]);
assert_eq!(
job.required.needs_tools,
vec!["clang".to_string(), "ld.lld".to_string()],
"sorted and deduped so the same request preflights identically"
);
assert!(job.detail.contains("clang, ld.lld"), "{}", job.detail);
let requirement = job.required.to_requirement(1);
assert_eq!(
requirement.needs_tools,
vec!["clang".to_string(), "ld.lld".to_string()]
);
}
#[test]
fn msvc_target_requires_a_windows_host() {
assert_eq!(
required_os_for_command("cargo build --target x86_64-pc-windows-msvc"),
Some("windows".to_string())
);
assert_eq!(
required_os_for_command("cargo build --target=aarch64-pc-windows-msvc --release"),
Some("windows".to_string())
);
}
#[test]
fn cargo_xwin_keeps_rust_target_requirements_without_a_native_windows_gate() {
for command in [
"cargo xwin build --target x86_64-pc-windows-msvc",
"cargo +nightly-2026-08-31 xwin build --target=aarch64-pc-windows-msvc",
"cargo-xwin xwin build --target x86_64-pc-windows-msvc",
] {
let result = preflight(command, true);
assert!(result.is_compilation, "{command}");
assert!(result.required.needs_cargo, "{command}");
assert_eq!(result.family.as_deref(), Some("cargo_build"));
assert_eq!(result.required.needs_targets.len(), 1);
assert!(result.required.needs_targets[0].contains("-windows-msvc"));
assert_eq!(result.required.needs_os, None);
assert_eq!(required_os_for_command(command), None);
}
}
#[test]
fn cargo_xwin_does_not_relax_other_commands_or_apple_targets() {
for command in [
"cargo xwin build --target x86_64-pc-windows-msvc && cargo build --target x86_64-pc-windows-msvc",
"cargo --config xwin build --target x86_64-pc-windows-msvc",
"cargo build --manifest-path cargo-xwin --target x86_64-pc-windows-msvc",
] {
assert_eq!(
required_os_for_command(command).as_deref(),
Some("windows"),
"{command}"
);
assert_eq!(
preflight(command, true).required.needs_os.as_deref(),
Some("windows"),
"{command}"
);
}
for command in [
"cargo xwin build --target aarch64-apple-darwin",
"cargo xwin build --target x86_64-pc-windows-msvc && cargo build --target aarch64-apple-darwin",
] {
assert_eq!(
required_os_for_command(command).as_deref(),
Some("darwin"),
"{command}"
);
assert_eq!(
preflight(command, true).required.needs_os.as_deref(),
Some("darwin"),
"{command}"
);
}
}
#[test]
fn darwin_zigbuild_keeps_linker_and_target_requirements_without_native_host_gate() {
for command in [
"cargo zigbuild --target aarch64-apple-darwin",
"cargo +nightly zigbuild --target=x86_64-apple-darwin",
"cargo-zigbuild zigbuild --target aarch64-apple-darwin",
"env SDKROOT=/opt/MacOSX.sdk cargo zigbuild --target aarch64-apple-darwin",
] {
let result = preflight(command, true);
assert!(result.is_compilation, "{command}");
assert!(result.required.needs_cargo, "{command}");
assert!(result.required.needs_zig, "{command}");
assert_eq!(result.family.as_deref(), Some("cargo_zigbuild"));
assert_eq!(result.required.needs_targets.len(), 1);
assert!(result.required.needs_targets[0].ends_with("-apple-darwin"));
assert_eq!(result.required.needs_os, None);
assert_eq!(required_os_for_command(command), None);
}
}
#[test]
fn darwin_zigbuild_does_not_relax_native_commands_or_other_apple_targets() {
for command in [
"cargo build --target aarch64-apple-darwin",
"cargo build --manifest-path cargo-zigbuild --target aarch64-apple-darwin",
"cargo --config zigbuild build --target aarch64-apple-darwin",
"cargo zigbuild --target aarch64-apple-ios",
"cargo zigbuild --target aarch64-apple-darwin && cargo build --target aarch64-apple-darwin",
] {
assert_eq!(
required_os_for_command(command).as_deref(),
Some("darwin"),
"{command}"
);
assert_eq!(
preflight(command, true).required.needs_os.as_deref(),
Some("darwin"),
"{command}"
);
}
let command = "cargo zigbuild --target x86_64-pc-windows-msvc";
assert_eq!(required_os_for_command(command).as_deref(), Some("windows"));
assert_eq!(
preflight(command, true).required.needs_os.as_deref(),
Some("windows")
);
}
#[test]
fn cross_compilable_targets_stay_unconstrained() {
assert_eq!(
required_os_for_command("cargo build --target x86_64-pc-windows-gnu"),
None
);
assert_eq!(
required_os_for_command("cargo build --target wasm32-unknown-unknown"),
None
);
assert_eq!(required_os_for_command("cargo check --workspace"), None);
assert_eq!(required_os_for_command("cargo test"), None);
}
#[test]
fn apple_target_requires_a_darwin_host() {
for triple in [
"aarch64-apple-darwin",
"x86_64-apple-darwin",
"aarch64-apple-ios",
"aarch64-apple-tvos",
"arm64_32-apple-watchos",
] {
assert_eq!(
required_os_for_command(&format!("cargo build --target {triple}")),
Some("darwin".to_string()),
"triple={triple}"
);
}
}
#[test]
fn conflicting_targets_constrain_nothing() {
assert_eq!(
required_os_for_command(
"cargo build --target x86_64-pc-windows-msvc && cargo build --target aarch64-apple-darwin"
),
None
);
}
#[test]
fn preflight_surfaces_the_required_os() {
let p = preflight("cargo build --target x86_64-pc-windows-msvc", false);
assert_eq!(p.required.needs_os.as_deref(), Some("windows"));
assert_eq!(
p.required.to_requirement(0).needs_os.as_deref(),
Some("windows")
);
let plain = preflight("cargo build --release", false);
assert_eq!(plain.required.needs_os, None);
assert_eq!(plain.required.to_requirement(0).needs_os, None);
}
#[test]
fn common_cargo_build_offloads_needing_cargo() {
let p = preflight("cargo build --release", false);
assert!(p.is_compilation);
assert_eq!(p.family.as_deref(), Some("cargo_build"));
assert!(p.required.needs_cargo);
assert!(!p.required.needs_bun);
assert_eq!(p.base_recommendation, AdmitRecommendation::Offload);
}
#[test]
fn wasm_target_is_captured_as_required_capability() {
let p = preflight("cargo build --target wasm32-unknown-unknown", false);
assert!(p.required.needs_cargo);
assert_eq!(p.required.needs_targets, vec!["wasm32-unknown-unknown"]);
let p2 = preflight("cargo test --target=aarch64-apple-darwin", false);
assert_eq!(p2.required.needs_targets, vec!["aarch64-apple-darwin"]);
}
#[test]
fn explicit_toolchain_override_is_captured() {
let p = preflight("cargo +nightly-2025-11-01 build", false);
assert_eq!(p.required.needs_toolchains, vec!["nightly-2025-11-01"]);
}
#[test]
fn bun_command_needs_bun() {
let p = preflight("bun test", false);
if p.is_compilation {
assert!(p.required.needs_bun);
assert!(!p.required.needs_cargo);
assert_eq!(p.base_recommendation, AdmitRecommendation::Offload);
} else {
assert_eq!(p.base_recommendation, AdmitRecommendation::Local);
}
}
#[test]
fn non_compilation_runs_local() {
let p = preflight("ls -la", false);
assert!(!p.is_compilation);
assert_eq!(p.base_recommendation, AdmitRecommendation::Local);
assert!(!p.required.needs_cargo && !p.required.needs_bun);
assert!(p.detail.contains("locally"));
}
#[test]
fn quoted_exec_form_is_classified_decisively() {
let p = preflight("sh -c 'cargo build'", false);
assert!(matches!(
p.base_recommendation,
AdmitRecommendation::Offload | AdmitRecommendation::Local
));
}
#[test]
fn compound_shell_form_is_split_and_unioned() {
let p = preflight("cargo build && bun test", false);
assert!(
p.compound.len() >= 2,
"compound must be split: {:?}",
p.compound
);
if p.is_compilation {
assert!(p.required.needs_cargo || p.required.needs_bun);
}
}
#[test]
fn refine_offload_when_some_candidate_admissible() {
let summary = aggregate_rejections(
3,
&[CandidateRejection {
worker_id: "a".into(),
category: AdmissionRejectionCategory::InsufficientSlots,
}],
);
assert_eq!(
refine_recommendation(AdmitRecommendation::Offload, &summary),
AdmitRecommendation::Offload
);
}
#[test]
fn refine_queue_when_all_rejected_for_health() {
let summary = aggregate_rejections(
2,
&[
CandidateRejection {
worker_id: "a".into(),
category: AdmissionRejectionCategory::InsufficientSlots,
},
CandidateRejection {
worker_id: "b".into(),
category: AdmissionRejectionCategory::CircuitOpen,
},
],
);
assert_eq!(
refine_recommendation(AdmitRecommendation::Offload, &summary),
AdmitRecommendation::Queue
);
}
#[test]
fn refine_defer_when_all_rejected_for_capability() {
let summary = aggregate_rejections(
2,
&[
CandidateRejection {
worker_id: "a".into(),
category: AdmissionRejectionCategory::MissingRustTarget,
},
CandidateRejection {
worker_id: "b".into(),
category: AdmissionRejectionCategory::OsArchMismatch,
},
],
);
assert_eq!(
refine_recommendation(AdmitRecommendation::Offload, &summary),
AdmitRecommendation::Defer
);
}
#[test]
fn refine_keeps_local_recommendation() {
let summary = aggregate_rejections(0, &[]);
assert_eq!(
refine_recommendation(AdmitRecommendation::Local, &summary),
AdmitRecommendation::Local
);
}
#[test]
fn required_capabilities_convert_to_requirement() {
let p = preflight("cargo build --target wasm32-unknown-unknown", false);
let req = p.required.to_requirement(3);
assert!(req.needs_cargo);
assert_eq!(req.min_protocol, 3);
assert_eq!(
req.needs_targets,
vec!["wasm32-unknown-unknown".to_string()]
);
}
#[test]
fn preflight_serializes_with_stable_tokens() {
let p = preflight("cargo build", false);
let value = serde_json::to_value(&p).unwrap();
assert_eq!(value["base_recommendation"], "offload");
assert_eq!(value["is_compilation"], true);
let back: AdmitPreflight = serde_json::from_value(value).unwrap();
assert_eq!(back, p);
}
}