use degenbot_config::FleetProfile;
use crate::budget::{
self, BudgetError, BudgetOverrides, FleetBudget, DEFAULT_POOL_STATE_UPDATER_SLOTS,
DEFAULT_SIM_SLOT_CAP, MIN_SOLVER_CPUS,
};
use crate::dispatcher::BootError;
pub const PLAN_ID: &str = "fleetplan/1";
pub const HOST_FLOOR_CORES: u64 = 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Binding {
Pinned,
Serial,
}
impl Binding {
#[must_use]
pub const fn label(self) -> &'static str {
match self {
Self::Pinned => "pinned",
Self::Serial => "logical",
}
}
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Self::Pinned => "pinned",
Self::Serial => "serial",
}
}
}
impl std::fmt::Display for Binding {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct FleetPlan {
pub id: &'static str,
pub binding: Binding,
pub oversubscribed: bool,
pub budget_cpus: f64,
pub tier_refusal: Option<BudgetError>,
}
impl FleetPlan {
pub fn projected_budget(&self, overrides: &BudgetOverrides) -> Result<FleetBudget, BootError> {
match self.binding {
Binding::Pinned if !self.oversubscribed => {
FleetBudget::derive(self.budget_cpus, overrides).map_err(BootError::from)
}
Binding::Pinned => Ok(pinned_budget_marked(self.budget_cpus, overrides)),
Binding::Serial => Ok(serial_budget(self.budget_cpus, overrides)),
}
}
#[must_use]
pub fn pending_serial_refusal(&self) -> BootError {
match self.tier_refusal {
Some(refusal) => BootError::Budget(refusal),
None => BootError::Invariant(
"the serial binding (the 2-5-core tier) lands with FF-T4 \
— forced serial refuses rather than run the pinned topology \
silently narrower",
),
}
}
}
pub fn plan(
quota_cpus: f64,
profile: FleetProfile,
overrides: &BudgetOverrides,
) -> Result<FleetPlan, BootError> {
#[expect(
clippy::cast_possible_truncation,
reason = "quota floors are small positive values (core counts)"
)]
#[expect(
clippy::cast_sign_loss,
reason = "the quota is floored at 1.0 before the cast"
)]
let quota_floor = quota_cpus.max(1.0).floor() as u64;
if quota_floor < HOST_FLOOR_CORES {
return Err(BootError::Budget(BudgetError::BelowHostFloor {
quota: quota_cpus,
}));
}
match profile {
FleetProfile::Auto => match FleetBudget::derive(quota_cpus, overrides) {
Ok(_) => {
validate_io_workers(Binding::Pinned, overrides)?;
Ok(FleetPlan {
id: PLAN_ID,
binding: Binding::Pinned,
oversubscribed: false,
budget_cpus: quota_cpus,
tier_refusal: None,
})
}
Err(refusal @ BudgetError::QuotaTooSmallForPinnedRoles { .. }) => {
validate_io_workers(Binding::Serial, overrides)?;
Ok(FleetPlan {
id: PLAN_ID,
binding: Binding::Serial,
oversubscribed: false,
budget_cpus: quota_cpus,
tier_refusal: Some(refusal),
})
}
Err(other) => Err(BootError::Budget(other)),
},
FleetProfile::Pinned => {
validate_io_workers(Binding::Pinned, overrides)?;
let (oversubscribed, tier_refusal) = match FleetBudget::derive(quota_cpus, overrides) {
Ok(_) => (false, None),
Err(refusal) => (true, Some(refusal)),
};
Ok(FleetPlan {
id: PLAN_ID,
binding: Binding::Pinned,
oversubscribed,
budget_cpus: quota_cpus,
tier_refusal,
})
}
FleetProfile::Serial => {
validate_io_workers(Binding::Serial, overrides)?;
Ok(FleetPlan {
id: PLAN_ID,
binding: Binding::Serial,
oversubscribed: false,
budget_cpus: quota_cpus,
tier_refusal: None,
})
}
}
}
fn validate_io_workers(binding: Binding, overrides: &BudgetOverrides) -> Result<(), BootError> {
let Some(requested) = overrides.ambient_io_workers else {
return Ok(());
};
let legal = match binding {
Binding::Pinned => requested >= 1,
Binding::Serial => requested == 1,
};
if legal {
return Ok(());
}
Err(BootError::Budget(BudgetError::IoWorkersOutOfBounds {
requested,
binding: binding.name(),
}))
}
fn pinned_budget_marked(quota_cpus: f64, overrides: &BudgetOverrides) -> FleetBudget {
#[expect(
clippy::cast_possible_truncation,
reason = "quota floors are small positive values (core counts)"
)]
#[expect(
clippy::cast_sign_loss,
reason = "the quota is floored at 1.0 before the cast"
)]
let quota_floor = quota_cpus.max(1.0).floor() as u64;
let reserve_cpus = overrides
.reserve_cpus
.unwrap_or(budget::DEFAULT_RESERVE_CPUS);
let ambient_cpus = overrides
.ambient_io_workers
.unwrap_or_else(|| ((quota_floor.saturating_sub(reserve_cpus)) / 4).max(1));
let (resolve_cpus, merge_cpus) = (1, 1);
let base = reserve_cpus + ambient_cpus + resolve_cpus + merge_cpus;
let solver_cpus = overrides
.solver_cpus
.unwrap_or_else(|| quota_floor.saturating_sub(base).max(MIN_SOLVER_CPUS));
let solve_headroom = overrides
.solve_headroom
.unwrap_or(degenbot_core::cpu_budget::DEFAULT_SOLVE_HEADROOM);
FleetBudget {
quota_cpus,
quota_floor,
reserve_cpus,
ambient_cpus,
resolve_cpus,
merge_cpus,
solver_cpus,
solver_pin_count: usize::try_from(quota_floor)
.unwrap_or(usize::MAX)
.saturating_sub(solve_headroom)
.max(1),
sim_slot_cap: overrides.sim_slot_cap.unwrap_or(DEFAULT_SIM_SLOT_CAP),
pool_state_updater_slots: overrides
.pool_state_updater_slots
.unwrap_or(DEFAULT_POOL_STATE_UPDATER_SLOTS),
#[expect(
clippy::cast_precision_loss,
reason = "share sums are small core counts, exact in f64"
)]
fractional_remainder: (quota_cpus - (base + solver_cpus) as f64).max(0.0),
}
}
fn serial_budget(quota_cpus: f64, overrides: &BudgetOverrides) -> FleetBudget {
#[expect(
clippy::cast_possible_truncation,
reason = "quota floors are small positive values (core counts)"
)]
#[expect(
clippy::cast_sign_loss,
reason = "the quota is floored at 1.0 before the cast"
)]
let quota_floor = quota_cpus.max(1.0).floor() as u64;
FleetBudget {
quota_cpus,
quota_floor,
reserve_cpus: overrides
.reserve_cpus
.unwrap_or(budget::DEFAULT_RESERVE_CPUS),
ambient_cpus: 1,
resolve_cpus: 1,
merge_cpus: 1,
solver_cpus: MIN_SOLVER_CPUS,
solver_pin_count: 1,
sim_slot_cap: overrides.sim_slot_cap.unwrap_or(DEFAULT_SIM_SLOT_CAP),
pool_state_updater_slots: overrides
.pool_state_updater_slots
.unwrap_or(DEFAULT_POOL_STATE_UPDATER_SLOTS),
#[expect(
clippy::cast_precision_loss,
reason = "the host floor is a tiny core count, exact in f64"
)]
fractional_remainder: (quota_cpus - HOST_FLOOR_CORES as f64).max(0.0),
}
}
#[cfg(test)]
#[expect(clippy::expect_used)]
mod tests {
use super::*;
use crate::dispatcher::SlotLayout;
use degenbot_config::FleetProfile;
fn overrides() -> BudgetOverrides {
BudgetOverrides::default()
}
#[test]
fn the_tier_table_is_the_contract() {
let err = plan(1.5, FleetProfile::Auto, &overrides()).expect_err("sub-2-core host");
assert!(
matches!(err, BootError::Budget(BudgetError::BelowHostFloor { quota }) if (quota - 1.5).abs() < f64::EPSILON),
"below-2-cores refuses with the typed host-floor error, got {err:?}"
);
let p = plan(2.0, FleetProfile::Auto, &overrides()).expect("2-core host plans");
assert_eq!(p.binding, Binding::Serial);
assert!(!p.oversubscribed);
assert_eq!(p.id, PLAN_ID);
let p = plan(5.99, FleetProfile::Auto, &overrides()).expect("5.99-core host plans");
assert_eq!(p.binding, Binding::Serial);
let p = plan(6.0, FleetProfile::Auto, &overrides()).expect("6-core host plans");
assert_eq!(p.binding, Binding::Pinned);
assert!(!p.oversubscribed);
let p = plan(2.0, FleetProfile::Pinned, &overrides()).expect("forced pinned plans");
assert_eq!(p.binding, Binding::Pinned);
assert!(
p.oversubscribed,
"a forced pinned binding below the floor is marked"
);
let p = plan(24.0, FleetProfile::Serial, &overrides()).expect("forced serial plans");
assert_eq!(p.binding, Binding::Serial);
assert!(!p.oversubscribed);
assert!(p.tier_refusal.is_none());
}
#[test]
fn the_auto_serial_tier_carries_the_pinned_refusal() {
let p = plan(4.0, FleetProfile::Auto, &overrides()).expect("4-core host plans");
assert_eq!(p.binding, Binding::Serial);
let refusal = p.tier_refusal.as_ref().expect("the tier refusal rides");
assert!(
matches!(
refusal,
BudgetError::QuotaTooSmallForPinnedRoles { quota, required }
if (*quota - 4.0).abs() < f64::EPSILON && *required == 6
),
"the carried refusal is the pinned derivation's own, got {refusal:?}"
);
assert!(matches!(
p.pending_serial_refusal(),
BootError::Budget(BudgetError::QuotaTooSmallForPinnedRoles { .. })
));
let forced = plan(24.0, FleetProfile::Serial, &overrides()).expect("forced serial plans");
assert!(matches!(
forced.pending_serial_refusal(),
BootError::Invariant(_)
));
}
#[test]
fn the_forced_pinned_oversubscription_carries_the_pinned_floor_refusal() {
let p = plan(4.0, FleetProfile::Pinned, &overrides()).expect("forced pinned plans");
assert_eq!(p.binding, Binding::Pinned);
assert!(p.oversubscribed);
let refusal = p
.tier_refusal
.as_ref()
.expect("the overridden refusal rides");
assert!(
matches!(
refusal,
BudgetError::QuotaTooSmallForPinnedRoles { quota, required }
if (*quota - 4.0).abs() < f64::EPSILON && *required == 6
),
"the carried refusal is the pinned derivation's own, got {refusal:?}"
);
for quota in [6.0_f64, 8.0, 24.0] {
let p = plan(quota, FleetProfile::Pinned, &overrides()).expect("eligible host plans");
assert!(!p.oversubscribed);
assert!(p.tier_refusal.is_none());
}
}
#[test]
fn io_workers_overrides_are_validated_against_plan_bounds() {
let ov = BudgetOverrides {
ambient_io_workers: Some(0),
..overrides()
};
let err = plan(8.0, FleetProfile::Pinned, &ov).expect_err("A=0 refuses");
assert!(
matches!(
err,
BootError::Budget(BudgetError::IoWorkersOutOfBounds {
requested: 0,
binding: "pinned"
})
),
"the out-of-bounds override names the request and the binding, got {err:?}"
);
let ov = BudgetOverrides {
ambient_io_workers: Some(2),
..overrides()
};
let err = plan(4.0, FleetProfile::Auto, &ov).expect_err("serial A=2 refuses");
assert!(
matches!(
err,
BootError::Budget(BudgetError::IoWorkersOutOfBounds {
requested: 2,
binding: "serial"
})
),
"the serial tier validates its one-lane bound, got {err:?}"
);
let ov = BudgetOverrides {
ambient_io_workers: Some(2),
..overrides()
};
let p = plan(8.0, FleetProfile::Auto, &ov).expect("8-core A=2 plans");
assert_eq!(p.binding, Binding::Pinned);
assert_eq!(
p.projected_budget(&ov)
.expect("projection derives")
.ambient_cpus,
2
);
}
#[test]
fn plan_projections_yield_legal_non_empty_layouts() {
for quota in [2.0_f64, 2.5, 4.0, 5.99, 24.0] {
let p = plan(quota, FleetProfile::Serial, &overrides()).expect("serial plans");
let b = p
.projected_budget(&overrides())
.expect("serial projection is total");
assert_eq!(b.solver_pin_count, 1, "serial-0: exactly one solve seat");
assert_eq!(b.resolve_cpus, 1);
assert!(b.pool_state_updater_slots >= 1);
assert!(b.sim_slot_cap >= 1, "sim per budget");
SlotLayout::of(&b).expect("the serial projection hosts a legal layout");
}
for quota in [2.0_f64, 4.0, 5.99] {
let p = plan(quota, FleetProfile::Pinned, &overrides()).expect("forced pinned plans");
assert!(p.oversubscribed);
let b = p
.projected_budget(&overrides())
.expect("the marked projection is total");
assert!(
b.solver_pin_count >= 1,
"station legality: the pin count floors at 1"
);
assert!(
(b.fractional_remainder - 0.0).abs() < 1e-9,
"oversubscribed: no spendable remainder"
);
SlotLayout::of(&b).expect("the marked projection hosts a legal layout");
}
}
#[test]
fn the_pinned_eligible_projection_is_the_one_derivation() {
for quota in [6.0_f64, 8.0, 24.0, 6.5] {
let p = plan(quota, FleetProfile::Auto, &overrides()).expect("eligible host plans");
assert_eq!(p.binding, Binding::Pinned);
assert_eq!(
p.projected_budget(&overrides())
.expect("projection derives"),
FleetBudget::derive(quota, &overrides()).expect("the derivation")
);
}
}
}