Skip to main content

degenbot_workers/
budget.rs

1//! `FleetBudget` — the ONE budget authority bounding the SUM (design doc
2//! §5; ADR-042 §3).
3//!
4//! Every consumer declares a peak-CPU share and a thread/slot count; the
5//! fleet refuses to boot when the declared shares exceed the quota —
6//! oversubscription is a configuration bug surfaced at boot, never a runtime
7//! throttle storm. Fractional-quota policy (reviewed in the ADR): detection
8//! keeps `degenbot_core::cpu_budget`'s ceil for worker-existence sizing,
9//! while allocation arithmetic FLOORS — integer shares sum against
10//! `floor(Q)` and the fractional remainder is spendable only by I/O-dominant
11//! consumers (`SimDriver` slots, ambient I/O), whose measured duty is
12//! partial-core by construction. `Q` comes from [`crate::quota`].
13//!
14//! # Discrepancy note (recorded, not redesigned)
15//!
16//! The design doc's worked 8-core table lists ambient `A = 2` while the
17//! rule column reads `max(1, floor((Q−H)/4))`, which evaluates to 1 at
18//! `Q = 8, H = 1` (leaving `S = 4`). This implementation follows the RULE
19//! literally; a deployed operator recovers the table's `A = 2, S = 3` split
20//! with `DEGENBOT_IO_WORKERS=2` (the terminal override both the table and
21//! this code honor). The SUM invariant holds under either assignment.
22//!
23//! # Pin count = the LPT bin count (P6YXA6 sizing reconciliation)
24//!
25//! Solver pins are STRUCTURAL, not a share multiple: one seat per LPT bin,
26//! the bin count following the same policy as
27//! `degenbot_core::cpu_budget`'s solve bins — `floor(Q)` minus
28//! [`degenbot_core::cpu_budget::DEFAULT_SOLVE_HEADROOM`]. At the deployed
29//! Q = 8 that is 6 pins, exactly the bin count every dispatch arm binds at
30//! (the ad-hoc `shares x 2` pin derivation — 8 seats at Q = 8 against
31//! 6 bins — retires with the hard cutover). Walk ADMISSION stays the
32//! share `S`: a gated bin parks, per design doc §5.
33//!
34//! # Cross-authority contract: allocation floors, detection ceils
35//!
36//! `degenbot_core::cpu_budget` CEILS fractional cgroup quotas for
37//! worker-existence sizing (a 4.5-core quota still buys a 5th worker);
38//! this authority FLOORS (`floor(Q) − headroom`), because Solver threads
39//! are never I/O-dominant and must never spend the fractional remainder.
40//! Consequence (property-tested in this file, `cross_authority`): the two
41//! sizing authorities agree at integer quotas with `affinity >= Q`; under
42//! a fractional quota with `affinity >= ceil(Q)` the legacy-stance solve
43//! bins sit exactly one ABOVE the fleet seats. Integer quotas are the
44//! deployment norm, and fleet-hosted cycles bind bins at the seat count
45//! anyway, so the divergence is inert in production.
46
47use degenbot_config::FleetConfig;
48
49/// Fixed reserve share `H` (Python bridge, pump, `OTel`, async GC): the fleet
50/// must never starve I/O.
51pub const DEFAULT_RESERVE_CPUS: u64 = 1;
52/// Minimum Solver share the fleet will host.
53pub const MIN_SOLVER_CPUS: u64 = 2;
54/// Today's `SimSlots` cap, preserved as the `SimDriver` slot cap (design doc §5).
55pub const DEFAULT_SIM_SLOT_CAP: usize = 4;
56/// The registration intake station's `PoolStateUpdater` slot cap (PRG-3,
57/// ADR-042 F2: the registration crawl's pool-build consumers hosted as
58/// keyed deferrable units). I/O-dominant by construction (RPC-bound
59/// builds), so the billing follows the `SimDriver` model exactly.
60pub const DEFAULT_POOL_STATE_UPDATER_SLOTS: usize = 4;
61
62/// Terminal, typed overrides (config/env) consumed by [`FleetBudget::derive`]
63/// a configured value wins, is logged, and participates in the same sum
64/// check (design doc §5: "overrides are terminal").
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
66pub struct BudgetOverrides {
67    /// `fleet.reserve_cpus` — reserve share `H`.
68    pub reserve_cpus: Option<u64>,
69    /// `runtime.io_workers` — ambient I/O runtime `A`.
70    pub ambient_io_workers: Option<u64>,
71    /// `fleet.solver_cpus` — Solver share `S`.
72    pub solver_cpus: Option<u64>,
73    /// `fleet.sim_slot_cap` — `SimDriver` slot count.
74    pub sim_slot_cap: Option<usize>,
75    /// `fleet.pool_state_updater_slots` — `PoolStateUpdater` slot count.
76    pub pool_state_updater_slots: Option<usize>,
77    /// Solve headroom `H_s` in the seat formula `floor(Q) − H_s` (the
78    /// documented floor-allocation constant; LW-T4 makes it an explicit
79    /// boot-authority input).
80    pub solve_headroom: Option<usize>,
81}
82
83impl BudgetOverrides {
84    /// The typed-config projection (env vars land in the same fields via
85    /// the loader's env layer).
86    #[must_use]
87    pub fn from_config(cfg: &degenbot_config::BotConfig) -> Self {
88        Self {
89            reserve_cpus: cfg.fleet.reserve_cpus.and_then(|v| u64::try_from(v).ok()),
90            ambient_io_workers: cfg.runtime.io_workers.and_then(|v| u64::try_from(v).ok()),
91            solver_cpus: cfg.fleet.solver_cpus.and_then(|v| u64::try_from(v).ok()),
92            sim_slot_cap: cfg.fleet.sim_slot_cap,
93            pool_state_updater_slots: cfg.fleet.pool_state_updater_slots,
94            // The typed T8 follow-through: the headroom override
95            // reaches the budget derive; unset stays None and the documented
96            // constant rules below.
97            solve_headroom: cfg.solve.solve_headroom,
98        }
99    }
100}
101
102/// Why a budget was refused.
103#[derive(Debug, Clone, Copy, PartialEq, thiserror::Error)]
104pub enum BudgetError {
105    /// Declared peak shares exceed the quota floor.
106    #[error(
107        "fleet budget oversubscribed: declared peak shares {declared} cores exceed \
108         floor(quota {quota}) = {floor} — oversubscription is a configuration bug, failed at boot"
109    )]
110    Oversubscribed {
111        /// The fractional quota (cores).
112        quota: f64,
113        /// `floor(quota)` the integer shares must sum to.
114        floor: u64,
115        /// The declared sum that exceeded it.
116        declared: u64,
117    },
118    /// Fractional quota below the pinned-role floor (`H+A+R+M+2`).
119    #[error(
120        "fractional quota {quota:.2} below the pinned-role floor of {required} cores \
121         (reserve H + ambient A + resolve R + merge M + the 2-core Solver minimum); \
122         the fleet cannot host the pinned latency roles there — the \
123         serial/sequential fallback DECISION POINT is here (reth \
124         has_enough_parallelism(): lane capability is explicit, never silently \
125         narrower); the fallback arm itself is a DOWNSTREAM decision (LW-T7)"
126    )]
127    QuotaTooSmallForPinnedRoles {
128        /// The fractional quota (cores).
129        quota: f64,
130        /// The minimum usable quota.
131        required: u64,
132    },
133    /// Solver share derives below the minimum.
134    #[error("fleet Solver share derives to {solver} < the {min}-core minimum")]
135    TooFewSolverCpus {
136        /// The derived/declared Solver share.
137        solver: u64,
138        /// [`MIN_SOLVER_CPUS`].
139        min: u64,
140    },
141    /// Fractional quota below the 2-core host floor (FLEETFLOOR FF-T2: one
142    /// core for I/O work, one core for solve work) — no binding can host
143    /// the fleet there, forced or not.
144    #[error(
145        "fractional quota {quota:.2} below the 2-core host floor (one core for I/O \
146         work, one core for solve work) — no binding can host the fleet there \
147         (raise the host CPU budget / affinity to at least 2 cores)"
148    )]
149    BelowHostFloor {
150        /// The fractional quota (cores).
151        quota: f64,
152    },
153    /// A `runtime.io_workers` override the resolved plan cannot honor
154    /// (FF-T2): below the ambient floor (A >= 1) on the pinned
155    /// binding, or off the serial binding\u0027s exactly-one ambient I/O lane.
156    /// Refused with a hint, never a silent clamp.
157    #[error(
158        "runtime.io_workers override {requested} is out of bounds for the {binding} \
159         binding (pinned: A >= 1, the ambient floor; serial: exactly one \
160         ambient I/O lane) — fix or drop the override, never a silent clamp"
161    )]
162    IoWorkersOutOfBounds {
163        /// The requested override.
164        requested: u64,
165        /// The binding the plan resolved (the label).
166        binding: &'static str,
167    },
168}
169
170impl BudgetError {
171    /// The typed variant NAME (the closed, greppable refusal vocabulary).
172    /// `runtime_status()`'s `tier_refused` string names the family the plan
173    /// fell from (FF-T5 addendum, 452GZC); the Display wording stays the
174    /// operator sentence with the quota, the floor, and the hint.
175    #[must_use]
176    pub const fn name(&self) -> &'static str {
177        match self {
178            Self::Oversubscribed { .. } => "Oversubscribed",
179            Self::QuotaTooSmallForPinnedRoles { .. } => "QuotaTooSmallForPinnedRoles",
180            Self::TooFewSolverCpus { .. } => "TooFewSolverCpus",
181            Self::BelowHostFloor { .. } => "BelowHostFloor",
182            Self::IoWorkersOutOfBounds { .. } => "IoWorkersOutOfBounds",
183        }
184    }
185}
186
187/// One consumer row of the boot allocation table (design doc §5) — declared
188/// `(peak_cpus, thread_count)` per the ADR's sum-bounding contract.
189#[derive(Debug, Clone, Copy, PartialEq)]
190pub struct ConsumerShare {
191    /// Consumer name for the boot log.
192    pub consumer: &'static str,
193    /// Declared peak CPU share (cores).
194    pub peak_cpus: u64,
195    /// Declared thread/slot count (may exceed the share for I/O-dominant
196    /// roles; the CPU share is what the authority bounds).
197    pub thread_count: usize,
198    /// Sizing rule in words (mirrors the worker-census `sizing` text).
199    pub sizing: &'static str,
200}
201
202/// The derived fleet allocation — every consumer's declared share, the sum
203/// check, and the fractional remainder policy (design doc §5).
204#[derive(Debug, Clone, Copy, PartialEq)]
205pub struct FleetBudget {
206    /// The fractional quota `Q` this budget was derived from (cores).
207    pub quota_cpus: f64,
208    /// `floor(Q)` — integer shares sum against this.
209    pub quota_floor: u64,
210    /// Reserve `H` (Python bridge, pump, `OTel`, async GC). Fixed default 1.
211    pub reserve_cpus: u64,
212    /// Ambient I/O runtime `A` = `max(1, floor((Q−H)/4))`, terminal override
213    /// `DEGENBOT_IO_WORKERS`.
214    pub ambient_cpus: u64,
215    /// Resolve `R` — fixed v1 (1 core, 12.4 ms/cycle measured).
216    pub resolve_cpus: u64,
217    /// Merge `M` — exactly one sidecar.
218    pub merge_cpus: u64,
219    /// Solver pins `S` = `floor(Q) − H − A − R − M` (or the terminal
220    /// override); `>= 2` or the boot fails.
221    pub solver_cpus: u64,
222    /// Solver pin seats: STRUCTURAL — one per LPT bin
223    /// (`floor(Q)` − `cpu_budget::DEFAULT_SOLVE_HEADROOM`; concurrent walk
224    /// admission stays the share `S`).
225    pub solver_pin_count: usize,
226    /// `SimDriver` slots (duty-counted, spendable from the fractional
227    /// remainder only), capped at today's `SimSlots` cap by default.
228    pub sim_slot_cap: usize,
229    /// `PoolStateUpdater` slots (duty-counted, spendable from the
230    /// fractional remainder only) — the registration intake station's
231    /// bounded per-role unit pool. Behind Solver precedence at dispatch.
232    pub pool_state_updater_slots: usize,
233    /// The fractional remainder `Q − Σ(shares)` — spendable ONLY by
234    /// I/O-dominant consumers, enforced by construction: it is never
235    /// included in the integer sum check.
236    pub fractional_remainder: f64,
237}
238
239/// The per-tier projection mode: ONE owner
240/// (`FleetBudget::project`) derives all three tier tables. The tier is MODE
241/// DATA, not a second derivation — plan.rs's pinned/marked/serial arms
242/// select a mode.
243#[derive(Debug, Clone, Copy, PartialEq, Eq)]
244pub(crate) enum BudgetMode {
245    /// The pinned-role derivation: integer shares sum-checked against
246    /// `floor(Q)`, failing fast with the typed [`BudgetError`]s.
247    Pinned,
248    /// The forced-pinned-below-floor projection: pinned arithmetic with no
249    /// sum enforcement (the plan's `oversubscribed` mark declares the
250    /// deficit).
251    PinnedMarked,
252    /// The serial (2-5 core) projection: one ambient I/O lane, one cycle
253    /// thread, exactly one solve seat.
254    Serial,
255}
256
257/// The 2-core host floor: one core for I/O work, one core for solve work.
258/// The plan's tier gate and the serial projection's logical thread count
259/// share this ONE owner.
260pub const HOST_FLOOR_CORES: u64 = 2;
261
262impl FleetBudget {
263    /// Derive the table for `quota_cpus` under the terminal overrides.
264    /// Fails loudly (the typed [`BudgetError`]s) on over-subscription or a
265    /// quota too small for the pinned latency roles.
266    ///
267    /// # Errors
268    /// Any of the [`BudgetError`] fail-fast conditions.
269    pub fn derive(quota_cpus: f64, overrides: &BudgetOverrides) -> Result<Self, BudgetError> {
270        Self::project(quota_cpus, overrides, BudgetMode::Pinned)
271    }
272
273    /// The ONE tier projection: `mode` selects the pinned,
274    /// forced-pinned-marked, or serial table, all built from the SAME shared
275    /// formulas (H, A, R, M, the structural pin count, the slot caps). Only
276    /// the pinned arm enforces the sum check; the other two are total by
277    /// contract (the mark / the shared-thread topology carry the deficit).
278    ///
279    /// # Errors
280    /// Any of the [`BudgetError`] fail-fast conditions — produced only by
281    /// [`BudgetMode::Pinned`].
282    #[expect(
283        clippy::cast_possible_truncation,
284        reason = "quota floors are small positive values (core counts)"
285    )]
286    #[expect(
287        clippy::cast_precision_loss,
288        reason = "core counts are exact in f64 at any realistic quota"
289    )]
290    #[expect(
291        clippy::cast_sign_loss,
292        reason = "the floor is clamped to >= 1.0 before the cast"
293    )]
294    pub(crate) fn project(
295        quota_cpus: f64,
296        overrides: &BudgetOverrides,
297        mode: BudgetMode,
298    ) -> Result<Self, BudgetError> {
299        let quota_floor = quota_cpus.max(1.0).floor() as u64;
300
301        // H — fixed default 1, terminal override.
302        let reserve_cpus = overrides.reserve_cpus.unwrap_or(DEFAULT_RESERVE_CPUS);
303
304        // A — the leftover-share rule, terminal DEGENBOT_IO_WORKERS override.
305        let ambient_cpus = overrides
306            .ambient_io_workers
307            .unwrap_or_else(|| ((quota_floor.saturating_sub(reserve_cpus)) / 4).max(1));
308
309        // R, M — fixed v1 consumers.
310        let (resolve_cpus, merge_cpus) = (1, 1);
311        let base = reserve_cpus + ambient_cpus + resolve_cpus + merge_cpus;
312
313        let solve_headroom = overrides
314            .solve_headroom
315            .unwrap_or(degenbot_core::cpu_budget::DEFAULT_SOLVE_HEADROOM);
316        let sim_slot_cap = overrides.sim_slot_cap.unwrap_or(DEFAULT_SIM_SLOT_CAP);
317        let pool_state_updater_slots = overrides
318            .pool_state_updater_slots
319            .unwrap_or(DEFAULT_POOL_STATE_UPDATER_SLOTS);
320        // Pin seats are STRUCTURAL (P6YXA6 reconciliation): one per LPT
321        // bin, the bin count following cpu_budget's solve-bin POLICY —
322        // minus the solve headroom, floored at 1 — but FLOORING the quota:
323        // cpu_budget ceils fractional quotas for worker-existence; Solver
324        // threads never spend the fractional remainder (§5).
325        // Walk admission stays the share S — a gated bin parks (§5).
326        let pinned_pin_count = usize::try_from(quota_floor)
327            .unwrap_or(usize::MAX)
328            .saturating_sub(solve_headroom)
329            .max(1);
330
331        match mode {
332            BudgetMode::Pinned => {
333                if base + MIN_SOLVER_CPUS > quota_floor {
334                    return Err(BudgetError::QuotaTooSmallForPinnedRoles {
335                        quota: quota_cpus,
336                        required: base + MIN_SOLVER_CPUS,
337                    });
338                }
339                // S — the leftover of the floor after the fixed consumers
340                // (or the terminal override, checked against the same sum).
341                let solver_cpus = overrides.solver_cpus.unwrap_or(quota_floor - base);
342                if base + solver_cpus > quota_floor {
343                    return Err(BudgetError::Oversubscribed {
344                        quota: quota_cpus,
345                        floor: quota_floor,
346                        declared: base + solver_cpus,
347                    });
348                }
349                if solver_cpus < MIN_SOLVER_CPUS {
350                    return Err(BudgetError::TooFewSolverCpus {
351                        solver: solver_cpus,
352                        min: MIN_SOLVER_CPUS,
353                    });
354                }
355                Ok(Self {
356                    quota_cpus,
357                    quota_floor,
358                    reserve_cpus,
359                    ambient_cpus,
360                    resolve_cpus,
361                    merge_cpus,
362                    solver_cpus,
363                    solver_pin_count: pinned_pin_count,
364                    sim_slot_cap,
365                    pool_state_updater_slots,
366                    fractional_remainder: quota_cpus - (base + solver_cpus) as f64,
367                })
368            }
369            BudgetMode::PinnedMarked => {
370                let solver_cpus = overrides
371                    .solver_cpus
372                    .unwrap_or_else(|| quota_floor.saturating_sub(base).max(MIN_SOLVER_CPUS));
373                Ok(Self {
374                    quota_cpus,
375                    quota_floor,
376                    reserve_cpus,
377                    ambient_cpus,
378                    resolve_cpus,
379                    merge_cpus,
380                    solver_cpus,
381                    solver_pin_count: pinned_pin_count,
382                    sim_slot_cap,
383                    pool_state_updater_slots,
384                    // Oversubscribed: no spendable remainder exists (the
385                    // deficit is the plan's mark, not a budget field) —
386                    // clamp at zero.
387                    fractional_remainder: (quota_cpus - (base + solver_cpus) as f64).max(0.0),
388                })
389            }
390            BudgetMode::Serial => Ok(Self {
391                quota_cpus,
392                quota_floor,
393                reserve_cpus,
394                // Exactly one ambient I/O lane (validated by the plan).
395                ambient_cpus: 1,
396                resolve_cpus,
397                merge_cpus,
398                // The logical 2-core solve minimum: the serial cycle thread
399                // runs solve work on the second core.
400                solver_cpus: MIN_SOLVER_CPUS,
401                // serial-0: exactly one solve seat (the FF-T4 contract).
402                solver_pin_count: 1,
403                sim_slot_cap,
404                pool_state_updater_slots,
405                // The binding owns two threads; everything above is spendable.
406                fractional_remainder: (quota_cpus - HOST_FLOOR_CORES as f64).max(0.0),
407            }),
408        }
409    }
410
411    /// H + A + R + M + S — the declared sum the authority bounds.
412    #[must_use]
413    pub const fn declared_sum(&self) -> u64 {
414        self.reserve_cpus
415            + self.ambient_cpus
416            + self.resolve_cpus
417            + self.merge_cpus
418            + self.solver_cpus
419    }
420
421    /// The boot allocation table (log/boot-dump consumer order).
422    #[must_use]
423    pub fn allocation_table(&self) -> Vec<ConsumerShare> {
424        vec![
425            ConsumerShare {
426                consumer: "reserve",
427                peak_cpus: self.reserve_cpus,
428                thread_count: 0,
429                sizing: "fixed; must never starve I/O",
430            },
431            ConsumerShare {
432                consumer: "ambient_io",
433                peak_cpus: self.ambient_cpus,
434                thread_count: usize::try_from(self.ambient_cpus).unwrap_or(1),
435                sizing: "max(1, floor((Q-H)/4)); DEGENBOT_IO_WORKERS override is terminal",
436            },
437            ConsumerShare {
438                consumer: "resolve",
439                peak_cpus: self.resolve_cpus,
440                thread_count: usize::try_from(self.resolve_cpus).unwrap_or(1),
441                sizing: "fixed v1 (12.4 ms/cycle measured)",
442            },
443            ConsumerShare {
444                consumer: "merge",
445                peak_cpus: self.merge_cpus,
446                thread_count: usize::try_from(self.merge_cpus).unwrap_or(1),
447                sizing: "exactly one sidecar",
448            },
449            ConsumerShare {
450                consumer: "solver",
451                peak_cpus: self.solver_cpus,
452                thread_count: self.solver_pin_count,
453                sizing: "Q - H - A - R - M (>= 2 or fail-fast); pins = one per LPT bin (floor(Q) - solve headroom)",
454            },
455        ]
456    }
457
458    /// Re-declare the shares under a changed quota (posture/logged event).
459    /// Pure re-derivation; the HOST decides which pins change (T9) and never
460    /// re-keys mid-cycle.
461    ///
462    /// # Errors
463    /// Any of the [`BudgetError`] fail-fast conditions under the new quota.
464    pub fn resize(
465        &self,
466        new_quota_cpus: f64,
467        new_overrides: &BudgetOverrides,
468    ) -> Result<Self, BudgetError> {
469        Self::derive(new_quota_cpus, new_overrides)
470    }
471
472    /// Whether moving from this budget to `next` requires pin re-keying
473    /// (a pin-count change) — the epoch-boundary rebalance trigger (T9).
474    #[must_use]
475    pub const fn pins_require_rekey(&self, next: &FleetBudget) -> bool {
476        self.solver_pin_count != next.solver_pin_count
477    }
478}
479
480/// Fractional quota detection seam: the typed `fleet.quota_cpus` override
481/// wins (terminal, per the §5 override rule); unset detects the real cgroup
482/// via [`crate::quota::fractional_cpu_budget`]. Tests inject values
483/// directly into [`FleetBudget::derive`].
484#[must_use]
485pub fn detected_quota_cpus(cfg: &FleetConfig) -> f64 {
486    cfg.quota_cpus
487        .filter(|q| *q >= 1.0)
488        .unwrap_or_else(crate::quota::fractional_cpu_budget)
489}
490
491#[cfg(test)]
492#[expect(clippy::expect_used)]
493mod tests {
494    use super::*;
495
496    fn overrides() -> BudgetOverrides {
497        BudgetOverrides::default()
498    }
499
500    #[test]
501    fn the_8_core_quota_table_sums_exactly_against_the_floor() {
502        let b = FleetBudget::derive(8.0, &overrides()).expect("8-core quota hostable");
503        assert_eq!(b.quota_floor, 8);
504        // SUM invariant: H + A + R + M + S = Q (design doc §5).
505        assert_eq!(b.declared_sum(), b.quota_floor);
506        assert_eq!(b.reserve_cpus, 1);
507        assert_eq!(b.resolve_cpus, 1);
508        assert_eq!(b.merge_cpus, 1);
509        // The rule column: A = max(1, floor((Q-H)/4)) = 1, leaving S = 4.
510        // (The doc TABLE's A=2/S=3 split is reachable via the terminal
511        // DEGENBOT_IO_WORKERS override — see the module discrepancy note.)
512        assert_eq!(b.ambient_cpus, 1);
513        assert_eq!(b.solver_cpus, 4);
514        // Pins are STRUCTURAL: one seat per LPT bin = floor(Q) - the solve
515        // headroom (allocation FLOORS the quota; cpu_budget's worker-
516        // existence detection ceils it — the cross-authority property in this file
517        // pins that split) — not the 2:1 parked-wait over-subscription
518        // (P6YXA6 sizing note).
519        assert_eq!(b.solver_pin_count, 6);
520    }
521
522    #[test]
523    fn the_worked_8_core_table_split_is_reachable_via_the_terminal_override() {
524        // DEGENBOT_IO_WORKERS=2 (the current deployment) reproduces the
525        // doc §5 worked table exactly: A=2, S=3, sum 8.
526        let b = FleetBudget::derive(
527            8.0,
528            &BudgetOverrides {
529                ambient_io_workers: Some(2),
530                ..overrides()
531            },
532        )
533        .expect("hostable");
534        assert_eq!(b.ambient_cpus, 2);
535        assert_eq!(b.solver_cpus, 3);
536        assert_eq!(b.solver_pin_count, 6);
537        assert_eq!(b.declared_sum(), 8);
538    }
539
540    #[test]
541    fn a_quota_below_the_pinned_role_floor_fails_fast() {
542        // 4.5-core quota: floor is 4 but H+A+R+M+2 = 6 > 4 — the two pinned
543        // latency roles cannot be hosted there (the integer sum floors; the
544        // fractional remainder is never part of the sum check).
545        let err = FleetBudget::derive(4.5, &overrides()).expect_err("too small");
546        assert!(matches!(
547            err,
548            BudgetError::QuotaTooSmallForPinnedRoles { .. }
549        ));
550    }
551
552    /// FF-T5 addendum (452GZC): the typed refusal family carries a closed,
553    /// greppable NAME vocabulary — the runtime status names the family it
554    /// fell from (never a free-text parse); the wording stays the operator
555    /// sentence.
556    #[test]
557    fn the_refusal_family_names_its_typed_variants() {
558        let cases: [(BudgetError, &str); 5] = [
559            (
560                BudgetError::Oversubscribed {
561                    quota: 4.0,
562                    floor: 4,
563                    declared: 7,
564                },
565                "Oversubscribed",
566            ),
567            (
568                BudgetError::QuotaTooSmallForPinnedRoles {
569                    quota: 4.0,
570                    required: 6,
571                },
572                "QuotaTooSmallForPinnedRoles",
573            ),
574            (
575                BudgetError::TooFewSolverCpus {
576                    solver: 1,
577                    min: MIN_SOLVER_CPUS,
578                },
579                "TooFewSolverCpus",
580            ),
581            (BudgetError::BelowHostFloor { quota: 1.5 }, "BelowHostFloor"),
582            (
583                BudgetError::IoWorkersOutOfBounds {
584                    requested: 0,
585                    binding: "pinned",
586                },
587                "IoWorkersOutOfBounds",
588            ),
589        ];
590        for (err, expected) in cases {
591            assert_eq!(err.name(), expected, "{expected} names itself");
592            // The Display sentence never doubles as the family name — the
593            // tier_refused string composes them ("Name: message").
594            assert!(
595                !err.to_string().contains(expected),
596                "the wording stays the sentence; the name rides explicitly: {err}"
597            );
598        }
599    }
600
601    #[test]
602    fn fractional_quota_banks_the_remainder_outside_the_integer_sum() {
603        // 6.5-core quota: floor 6, base (H1+A1+R1+M1) = 4, S = 2; the 0.5
604        // remainder banks outside the sum check (I/O-dominant spend only).
605        let b = FleetBudget::derive(6.5, &overrides()).expect("hostable");
606        assert_eq!(b.quota_floor, 6);
607        assert_eq!(b.declared_sum(), 6);
608        assert!((b.fractional_remainder - 0.5).abs() < 1e-9);
609        assert_eq!(b.solver_cpus, 2);
610        assert_eq!(b.solver_pin_count, 4);
611    }
612
613    // ---- LW-T4 (Seam A+G1): boot authority — quota-derived sizing, no ambient fallback
614
615    /// The documented floor-allocation formula is the SOLE seat authority:
616    /// `pins == floor(Q) − solve_headroom` (floored at 1) with the shares
617    /// and the fractional remainder derived from the SAME quota — over the
618    /// matrix quota × headroom, under INJECTED quotas only (no detector may
619    /// run in tests): the test passes host-hardware-independently (cgroup-
620    /// limited CI and a bare host alike). A quota below the capacity floor
621    /// refuses TYPED, naming the floor and the serial/sequential fallback
622    /// decision point — never a silently narrower lane.
623    #[test]
624    fn seat_count_is_quota_headroom_derived_under_the_documented_formula_only() {
625        for quota in [1.0_f64, 1.5, 4.0, 8.0, 24.0] {
626            for headroom in [1_usize, 2] {
627                let overrides = BudgetOverrides {
628                    solve_headroom: Some(headroom),
629                    ..overrides()
630                };
631                // Test-quotas are positive reals (1.0..24.0): floor() is exact
632                // and the cast cannot lose sign or magnitude here.
633                #[expect(
634                    clippy::cast_possible_truncation,
635                    reason = "quota floors are exact for the injected positive reals"
636                )]
637                #[expect(
638                    clippy::cast_sign_loss,
639                    reason = "the injected quotas are all strictly positive"
640                )]
641                let q_floor = quota.max(1.0).floor() as u64;
642                // Boot OR refuse, never a silent mis-size: the pinned-role
643                // floor (H+A+R+M+2) and the headroom+1 floor both refuse
644                // TYPED, naming the floor and the serial fallback point.
645                let b = match FleetBudget::derive(quota, &overrides) {
646                    Ok(b) => b,
647                    Err(err @ BudgetError::QuotaTooSmallForPinnedRoles { .. }) => {
648                        let msg = err.to_string();
649                        assert!(
650                            msg.contains(&format!("{q_floor} numerically"))
651                                || msg.contains("pinned-role floor"),
652                            "the capacity-floor error must name the floor: {msg}"
653                        );
654                        assert!(
655                            msg.contains("serial/sequential"),
656                            "the typed floor error must name the serial/sequential fallback decision point (reth has_enough_parallelism): {msg}"
657                        );
658                        continue;
659                    }
660                    Err(other) => {
661                        // Test-fixture tripwire: an unexpected refusal here is
662                        // the failure — panic IS the assertion.
663                        #[expect(
664                            clippy::panic,
665                            reason = "an unexpected typed refusal is the fixture's failure mode"
666                        )]
667                        {
668                            panic!("unexpected typed refusal at quota {quota}: {other}")
669                        }
670                    }
671                };
672                assert!(
673                    q_floor > u64::try_from(headroom).unwrap_or(0),
674                    "a quota below headroom+1 must have taken the typed floor arm above"
675                );
676                assert_eq!(
677                    b.solver_pin_count as u64,
678                    q_floor
679                        .saturating_sub(u64::try_from(headroom).unwrap_or(0))
680                        .max(1),
681                    "seat count must be f(quota, headroom): quota {quota}, headroom {headroom}"
682                );
683                assert_eq!(
684                    b.declared_sum(),
685                    q_floor,
686                    "the integer share sum must be exactly floor(Q)"
687                );
688                // Exact-by-construction: the injected quotas are binary-exact
689                // (1.0/1.5/4.0/8.0/24.0) and floor(Q) is an integer, so the
690                // subtraction loses nothing — a strict comparison is exact.
691                {
692                    // The fills are an exact small integer; q_floor < 2^24 for
693                    // every injected quota, so the u32 conversion cannot lose.
694                    let q = u32::try_from(q_floor).expect("injected quotas are < 2^24");
695                    let quoted = quota - f64::from(q);
696                    assert!(
697                        (b.fractional_remainder - quoted).abs() < 1e-12,
698                        "the fractional remainder is Q − floor(Q), spendable only by I/O:                          {} vs {quoted}",
699                        b.fractional_remainder
700                    );
701                }
702            }
703        }
704    }
705
706    /// LW-T4 (reth research lesson-4): the boot authority never falls back to
707    /// ambient host state — `available_parallelism` must not appear in the
708    /// sizing paths (the DETECTION ceilings live in degenbot-core / quota.rs;
709    /// the seat authority floors purely from the injected quota). This is a
710    /// DELIBERATE regular-expression tripwire mirroring the pyo3-free
711    /// dependency assertion — replace it with a behavioral assertion if
712    /// budget.rs ever sizes off a parameterizable source instead of the
713    /// injected quota.
714    #[test]
715    fn seat_sizing_never_reads_ambient_parallelism() {
716        let src = std::fs::read_to_string(concat!(env!("CARGO_MANIFEST_DIR"), "/src/budget.rs"))
717            .expect("crate source readable");
718        // Scan ONLY the lib code: the tests module legitimately names the
719        // identifier (this assertion's own text).
720        let lib = src
721            .split("#[cfg(test)]")
722            .next()
723            .expect("the lib segment always exists");
724        assert!(
725            !lib.contains("available_parallelism"),
726            "the boot authority must never size lanes off ambient parallelism (reth lesson-4)"
727        );
728    }
729
730    /// LW-T4: oversubscription is TYPED at boot (derive returns the error —
731    /// `abort_executor` is reserved for RUNTIME strand abandonment only)
732    /// and the message names BOTH numbers (declared sum AND quota floor).
733    #[test]
734    fn oversubscription_is_typed_at_boot_and_names_both_numbers() {
735        let err = FleetBudget::derive(
736            8.0,
737            &BudgetOverrides {
738                ambient_io_workers: Some(2),
739                solver_cpus: Some(4),
740                ..overrides()
741            },
742        )
743        .expect_err("H1+A2+R1+M1+S4 = 9 > 8");
744        assert!(matches!(err, BudgetError::Oversubscribed { .. }));
745        let msg = err.to_string();
746        assert!(
747            msg.contains('9'),
748            "the message must name the declared sum: {msg}"
749        );
750        assert!(
751            msg.contains('8'),
752            "the message must name the quota floor: {msg}"
753        );
754    }
755
756    /// LW-T4: quota below the capacity floor is a TYPED refusal naming the
757    /// floor and pointing at the serial/sequential fallback decision point
758    /// (reth `has_enough_parallelism()` lesson: lane capability is explicit,
759    /// never silently narrower). The fallback arm itself is LW-T7's.
760    #[test]
761    fn quota_below_the_capacity_floor_refuses_typed_naming_the_fallback_point() {
762        let err = FleetBudget::derive(2.0, &overrides())
763            .expect_err("quota 2.0 < the pinned-role floor must refuse typed");
764        assert!(matches!(
765            err,
766            BudgetError::QuotaTooSmallForPinnedRoles { .. }
767        ));
768        let msg = err.to_string();
769        assert!(
770            msg.contains("serial"),
771            "the typed floor error must name the serial/sequential fallback decision point: {msg}"
772        );
773    }
774
775    #[test]
776    fn oversubscription_by_overrides_fails_at_boot() {
777        let err = FleetBudget::derive(
778            8.0,
779            &BudgetOverrides {
780                ambient_io_workers: Some(2),
781                solver_cpus: Some(4),
782                ..overrides()
783            },
784        )
785        .expect_err("H1+A2+R1+M1+S4 = 9 > 8");
786        assert!(matches!(err, BudgetError::Oversubscribed { .. }));
787    }
788
789    #[test]
790    fn a_sub_minimum_solver_share_is_refused() {
791        let err = FleetBudget::derive(
792            8.0,
793            &BudgetOverrides {
794                solver_cpus: Some(1),
795                ..overrides()
796            },
797        )
798        .expect_err("S=1 < the 2-core minimum");
799        assert!(matches!(err, BudgetError::TooFewSolverCpus { .. }));
800    }
801
802    #[test]
803    fn resize_redeclares_shares_and_keeps_the_sum_invariant() {
804        let big = FleetBudget::derive(8.0, &overrides()).expect("8");
805        let small = big.resize(6.5, &overrides()).expect("6.5 hostable");
806        assert_eq!(small.declared_sum(), small.quota_floor);
807        // Pin-count change flags the T9 rebalance (never a mid-cycle re-key).
808        assert!(big.pins_require_rekey(&small));
809        // Idempotent re-declaration under an unchanged quota.
810        let same = big.resize(8.0, &overrides()).expect("8 again");
811        assert!(!big.pins_require_rekey(&same));
812    }
813
814    #[test]
815    fn the_registration_intake_station_is_duty_counted_like_sim() {
816        // PoolStateUpdater slots default to the ADR-042 F2 station size and
817        // stay OUTSIDE the declared integer sum (I/O-dominant billing —
818        // exactly the SimDriver model).
819        let b = FleetBudget::derive(8.0, &overrides()).expect("hostable");
820        assert_eq!(b.pool_state_updater_slots, DEFAULT_POOL_STATE_UPDATER_SLOTS);
821        assert_eq!(b.declared_sum(), b.quota_floor);
822        // The terminal override wins (same terminal rule as the rest).
823        let b2 = FleetBudget::derive(
824            8.0,
825            &BudgetOverrides {
826                pool_state_updater_slots: Some(6),
827                ..overrides()
828            },
829        )
830        .expect("hostable");
831        assert_eq!(b2.pool_state_updater_slots, 6);
832        assert_eq!(b2.declared_sum(), b2.quota_floor);
833    }
834
835    #[test]
836    fn the_pool_state_updater_override_projects_from_the_typed_config() {
837        let mut cfg = degenbot_config::BotConfig::default();
838        cfg.fleet.pool_state_updater_slots = Some(6);
839        let o = BudgetOverrides::from_config(&cfg);
840        assert_eq!(o.pool_state_updater_slots, Some(6));
841        let b = FleetBudget::derive(8.0, &o).expect("hostable");
842        assert_eq!(b.pool_state_updater_slots, 6);
843    }
844
845    #[test]
846    fn allocation_table_covers_every_consumer_row() {
847        let b = FleetBudget::derive(8.0, &overrides()).expect("8");
848        let consumers: Vec<&str> = b
849            .allocation_table()
850            .into_iter()
851            .map(|s| s.consumer)
852            .collect();
853        for want in ["reserve", "ambient_io", "resolve", "merge", "solver"] {
854            assert!(consumers.contains(&want), "missing {want}");
855        }
856    }
857
858    #[test]
859    fn typed_config_projection_carries_the_override_fields() {
860        let mut cfg = degenbot_config::BotConfig::default();
861        cfg.fleet.solver_cpus = Some(3);
862        cfg.runtime.io_workers = Some(2);
863        cfg.fleet.sim_slot_cap = Some(6);
864        let o = BudgetOverrides::from_config(&cfg);
865        assert_eq!(o.solver_cpus, Some(3));
866        assert_eq!(o.ambient_io_workers, Some(2));
867        assert_eq!(o.sim_slot_cap, Some(6));
868        let b = FleetBudget::derive(8.0, &o).expect("hostable");
869        assert_eq!(b.solver_cpus, 3);
870        assert_eq!(b.sim_slot_cap, 6);
871    }
872
873    /// the `solve.solve_headroom` typed key reaches the budget
874    /// derive end-to-end — through the LOADER route (not just a hand-built
875    /// struct) — while the default stays `None` (the documented constant
876    /// rules below).
877    #[test]
878    fn the_solve_headroom_override_projects_from_the_typed_config() {
879        let loaded = degenbot_config::BotConfigLoader::new()
880            .without_env()
881            .with_cli("solve.solve_headroom", "2")
882            .load()
883            .expect("the headroom override must load from the explicit CLI layer");
884        assert_eq!(loaded.config.solve.solve_headroom, Some(2));
885        let o = BudgetOverrides::from_config(&loaded.config);
886        assert_eq!(o.solve_headroom, Some(2));
887        let b = FleetBudget::derive(8.0, &o).expect("hostable");
888        // The documented formula: pins = floor(Q) - H_s (8 - 2 = 6).
889        assert_eq!(b.solver_pin_count, 6);
890        assert_eq!(b.declared_sum(), b.quota_floor);
891        // The default config projects None — the documented constant rules.
892        assert_eq!(
893            BudgetOverrides::from_config(&degenbot_config::BotConfig::default()).solve_headroom,
894            None
895        );
896    }
897
898    /// GAXX2Z helper: the shared ambient formula A = max(1, (floor(Q)-H)/4).
899    fn ambient_formula(floor: u64, reserve: u64) -> u64 {
900        ((floor.saturating_sub(reserve)) / 4).max(1)
901    }
902
903    /// GAXX2Z helper: the structural pin formula floor(Q) - headroom, >= 1.
904    fn pin_formula(floor: u64, headroom: usize) -> usize {
905        usize::try_from(floor)
906            .unwrap_or(usize::MAX)
907            .saturating_sub(headroom)
908            .max(1)
909    }
910
911    /// GAXX2Z helper: the pinned arm's sum-checked invariants.
912    #[expect(
913        clippy::cast_precision_loss,
914        reason = "test quotas and small share sums are exact in f64"
915    )]
916    fn assert_pinned_projection(
917        quota: f64,
918        floor: u64,
919        ov: &BudgetOverrides,
920        reserve: u64,
921        ambient: u64,
922        pins: usize,
923    ) {
924        let base = reserve + ambient + 2;
925        match FleetBudget::project(quota, ov, BudgetMode::Pinned) {
926            Ok(b) => {
927                assert_eq!(b.reserve_cpus, reserve, "pinned reserve H");
928                assert_eq!(
929                    b.ambient_cpus, ambient,
930                    "pinned ambient A = max(1, (Q-H)/4)"
931                );
932                assert_eq!(
933                    b.solver_cpus,
934                    ov.solver_cpus.unwrap_or(floor - base),
935                    "pinned solver S = floor(Q) - H - A - R - M"
936                );
937                assert_eq!(
938                    b.solver_pin_count, pins,
939                    "pinned pins = floor(Q) - headroom"
940                );
941                assert!(
942                    b.declared_sum() <= floor,
943                    "pinned sum-check never oversubscribes floor(quota)"
944                );
945                if ov.solver_cpus.is_none() {
946                    assert_eq!(b.declared_sum(), floor, "pinned default fills floor(quota)");
947                }
948                assert!(
949                    (b.fractional_remainder - (quota - b.declared_sum() as f64)).abs() < 1e-9,
950                    "pinned remainder banks Q - declared_sum"
951                );
952            }
953            Err(refusal) => assert!(
954                matches!(
955                    refusal,
956                    BudgetError::QuotaTooSmallForPinnedRoles { .. }
957                        | BudgetError::Oversubscribed { .. }
958                        | BudgetError::TooFewSolverCpus { .. }
959                ),
960                "pinned refusal is a typed budget error, got {refusal:?}"
961            ),
962        }
963    }
964
965    /// GAXX2Z helper: the marked arm's total pinned arithmetic.
966    fn assert_marked_projection(
967        quota: f64,
968        floor: u64,
969        ov: &BudgetOverrides,
970        reserve: u64,
971        ambient: u64,
972        pins: usize,
973    ) {
974        let base = reserve + ambient + 2;
975        let b = FleetBudget::project(quota, ov, BudgetMode::PinnedMarked).expect("marked total");
976        assert_eq!(b.reserve_cpus, reserve, "marked reserve H");
977        assert_eq!(
978            b.ambient_cpus, ambient,
979            "marked ambient A = max(1, (Q-H)/4)"
980        );
981        assert_eq!(
982            b.solver_cpus,
983            ov.solver_cpus
984                .unwrap_or_else(|| floor.saturating_sub(base).max(MIN_SOLVER_CPUS)),
985            "marked solver S = max(floor(Q) - base, MIN)"
986        );
987        assert_eq!(
988            b.solver_pin_count, pins,
989            "marked pins = floor(Q) - headroom"
990        );
991        assert!(
992            b.fractional_remainder >= 0.0,
993            "marked remainder clamps at zero"
994        );
995    }
996
997    /// GAXX2Z helper: the serial arm's one-lane / one-seat topology.
998    #[expect(
999        clippy::cast_precision_loss,
1000        reason = "the host floor is a tiny core count, exact in f64"
1001    )]
1002    fn assert_serial_projection(quota: f64, ov: &BudgetOverrides, reserve: u64) {
1003        let b = FleetBudget::project(quota, ov, BudgetMode::Serial).expect("serial total");
1004        assert_eq!(b.reserve_cpus, reserve, "serial reserve H");
1005        assert_eq!(
1006            b.ambient_cpus, 1,
1007            "serial owns exactly one ambient I/O lane"
1008        );
1009        assert_eq!(
1010            b.solver_cpus, MIN_SOLVER_CPUS,
1011            "serial logical 2-core solve"
1012        );
1013        assert_eq!(b.solver_pin_count, 1, "serial-0: exactly one solve seat");
1014        assert!(
1015            (b.fractional_remainder - (quota - HOST_FLOOR_CORES as f64).max(0.0)).abs() < 1e-9,
1016            "serial remainder is Q - the 2-core host floor"
1017        );
1018    }
1019
1020    /// ONE projection owner. All three tier projections are produced
1021    /// by `FleetBudget::project(mode)` from the SAME shared formulas; this
1022    /// pin walks representative quotas and override shapes and asserts each
1023    /// mode's invariants against the derive formulas (the sum-check vs
1024    /// floor(quota), the reserve/ambient/solver/pin relationships) — not
1025    /// against the function's own output.
1026    #[test]
1027    #[expect(
1028        clippy::cast_possible_truncation,
1029        reason = "quota floors are small positive values (core counts)"
1030    )]
1031    #[expect(
1032        clippy::cast_sign_loss,
1033        reason = "the test quota is floored at 1.0 before the cast"
1034    )]
1035    fn every_projection_mode_shares_the_derive_formulas() {
1036        let cases: [BudgetOverrides; 3] = [
1037            BudgetOverrides::default(),
1038            BudgetOverrides {
1039                ambient_io_workers: Some(2),
1040                ..BudgetOverrides::default()
1041            },
1042            BudgetOverrides {
1043                reserve_cpus: Some(2),
1044                solver_cpus: Some(3),
1045                ..BudgetOverrides::default()
1046            },
1047        ];
1048        for quota in [2.0_f64, 2.5, 4.0, 5.99, 6.0, 6.5, 8.0, 24.0, 33.25] {
1049            let floor = quota.max(1.0).floor() as u64;
1050            for ov in &cases {
1051                let reserve = ov.reserve_cpus.unwrap_or(DEFAULT_RESERVE_CPUS);
1052                let ambient = ov
1053                    .ambient_io_workers
1054                    .unwrap_or_else(|| ambient_formula(floor, reserve));
1055                let headroom = ov
1056                    .solve_headroom
1057                    .unwrap_or(degenbot_core::cpu_budget::DEFAULT_SOLVE_HEADROOM);
1058                let pins = pin_formula(floor, headroom);
1059                assert_pinned_projection(quota, floor, ov, reserve, ambient, pins);
1060                assert_marked_projection(quota, floor, ov, reserve, ambient, pins);
1061                assert_serial_projection(quota, ov, reserve);
1062            }
1063        }
1064    }
1065
1066    mod cross_authority {
1067        //! The settled contract between the two CPU
1068        //! sizing authorities over ARBITRARY quota shapes. Fleet allocation
1069        //! floors: `seats = max(1, floor(Q) - headroom)`. `cpu_budget`
1070        //! worker-existence ceils: `solve = max(1, min(ceil(cgroup Q),
1071        //! affinity) - headroom)`. Consequences (see the module-note
1072        //! addendum above): agreement iff the quota is integer AND affinity
1073        //! covers it; a fractional quota under affinity >= ceil(Q) leaves
1074        //! legacy-stance solve bins exactly ONE above the fleet seats.
1075        use super::*;
1076        use proptest::prelude::*;
1077
1078        prop_compose! {
1079            fn quota_shape()(base in 1u64..=512u64, half in 0u8..2u8, affinity in 1u64..=1024u64)
1080                -> (u64, u8, u64) {
1081                    (base, half, affinity)
1082                }
1083        }
1084
1085        proptest! {
1086            #![proptest_config(proptest::test_runner::Config::with_cases(512))]
1087            #[test]
1088            #[expect(
1089                clippy::cast_precision_loss,
1090                reason = "quota units (1e6 scale, <= 5e8) are exact in f64"
1091            )]
1092            fn pins_and_solve_workers_follow_the_documented_floor_ceil_split(
1093                (base, half, affinity) in quota_shape(),
1094            ) {
1095                // cgroup encodes the quota at 1_000_000-unit periods; a
1096                // half-step quota is a fractional f64 core count.
1097                let units = base * 1_000_000 + u64::from(half) * 500_000;
1098                let floor_q = units / 1_000_000;
1099                let fractional = units % 1_000_000 != 0;
1100                // effective_budget_from_with_roots: ceil the cgroup quota,
1101                // then min with affinity (the budget floored at 1).
1102                let budget = units.div_ceil(1_000_000).min(affinity).max(1);
1103                let budget_usize = usize::try_from(budget).unwrap_or(usize::MAX);
1104                let solve = degenbot_core::cpu_budget::solve_worker_count_from(
1105                    None, None, budget_usize,
1106                );
1107                let q = (units as f64) / 1_000_000.0;
1108
1109                if floor_q < 6 {
1110                    // Below the pinned-role floor (H+A+R+M+2) nothing hosts.
1111                    // (bound first: prop_assert stringifies its expression,
1112                    // and `{ .. }` from `matches!` would break the format
1113                    // string)
1114                    let refused = matches!(
1115                        FleetBudget::derive(q, &BudgetOverrides::default()),
1116                        Err(BudgetError::QuotaTooSmallForPinnedRoles { .. })
1117                    );
1118                    prop_assert!(refused);
1119                } else {
1120                    // derive fails only on over-subscription or a
1121                    // too-small quota; our defaults cannot oversubscribe
1122                    // (floor_q >= 6 => base + MIN_SOLVER_CPUS <= floor).
1123                    let Ok(b) = FleetBudget::derive(q, &BudgetOverrides::default()) else {
1124                        return Err(TestCaseError::fail(format!(
1125                            "hostable shape refused: q = {q}"
1126                        )));
1127                    };
1128                    // The P6YXA6 formula, as written.
1129                    prop_assert_eq!(
1130                        b.solver_pin_count,
1131                        usize::try_from((floor_q - 2).max(1)).unwrap_or(usize::MAX)
1132                    );
1133                    if !fractional && affinity >= floor_q {
1134                        // Integer quota, adequate affinity: agreement.
1135                        prop_assert_eq!(b.solver_pin_count, solve);
1136                    } else if fractional && budget == floor_q + 1 {
1137                        // Fractional quota under affinity >= ceil(Q):
1138                        // worker-existence buys exactly one more core than
1139                        // allocation spends; pins never bank the remainder.
1140                        prop_assert_eq!(solve, b.solver_pin_count + 1);
1141                    } else if !fractional && affinity < floor_q {
1142                        // Affinity-take-over: the (smaller) budget rules
1143                        // the solve bins; the seat count never shrinks.
1144                        prop_assert!(solve <= b.solver_pin_count);
1145                    } else if fractional && budget <= floor_q {
1146                        // Affinity clamped below the ceiling.
1147                        prop_assert!(solve <= b.solver_pin_count);
1148                    }
1149                    let _ = fractional; // documented above
1150                }
1151            }
1152        }
1153    }
1154
1155    mod derivation {
1156        //! CVURM7 : `derive` over ARBITRARY quotas AND
1157        //! overrides. Total over the input space: a typed `Ok` holding the
1158        //! invariants (integer shares sum to at most the floor; the
1159        //! fractional remainder banks exactly `Q - declared_sum`; seats
1160        //! follow the structural formula) or a typed `BudgetError`
1161        //! classifying the refusal. Never a panic, never a silently mis-sized
1162        //! budget. NOTE: with a terminal `solver_cpus` override BELOW the
1163        //! recorded leftover the banked remainder legitimately exceeds 1
1164        //! core (the sum check bounds over-subscription only; under-declared
1165        //! shares bank for I/O-dominant spend) — so no `< 1` bound here.
1166        use super::*;
1167        use proptest::prelude::*;
1168
1169        fn overrides_shape() -> impl Strategy<Value = BudgetOverrides> {
1170            (
1171                1u64..=64u64,
1172                0u64..=16u64,
1173                0u64..=64u64,
1174                0usize..=16usize,
1175                0usize..=16usize,
1176            )
1177                .prop_map(|(h, a, s, sim, psu)| BudgetOverrides {
1178                    reserve_cpus: Some(h),
1179                    ambient_io_workers: Some(a),
1180                    solver_cpus: Some(s),
1181                    sim_slot_cap: Some(sim),
1182                    pool_state_updater_slots: Some(psu),
1183                    solve_headroom: None,
1184                })
1185        }
1186
1187        proptest! {
1188            #![proptest_config(proptest::test_runner::Config::with_cases(512))]
1189            #[test]
1190            #[expect(
1191                clippy::cast_precision_loss,
1192                reason = "quota units (1e6 scale) and share sums (<= 640) are exact in f64"
1193            )]
1194            fn derive_is_total_and_typed_over_the_whole_input_space(
1195                quota_units in 1u64..=64_000_000u64,
1196                ov in overrides_shape(),
1197            ) {
1198                let q = (quota_units as f64) / 1_000_000.0;
1199                let floor_q = quota_units / 1_000_000;
1200                match FleetBudget::derive(q, &ov) {
1201                    Ok(b) => {
1202                        // Integer shares sum against (never over) the floor.
1203                        prop_assert!(b.declared_sum() <= b.quota_floor);
1204                        prop_assert_eq!(b.quota_floor, floor_q);
1205                        // The fractional remainder is banked exactly,
1206                        // never negative and never beyond the quota.
1207                        let sum_f = b.declared_sum() as f64;
1208                        prop_assert!((b.fractional_remainder - (q - sum_f)).abs() < 1e-9);
1209                        prop_assert!(b.fractional_remainder >= 0.0 && b.fractional_remainder <= q);
1210                        // Seats are structural: overrides never move them.
1211                        prop_assert_eq!(
1212                            b.solver_pin_count,
1213                            usize::try_from(floor_q).unwrap_or(usize::MAX)
1214                                .saturating_sub(degenbot_core::cpu_budget::DEFAULT_SOLVE_HEADROOM)
1215                                .max(1)
1216                        );
1217                    }
1218                    Err(
1219                        BudgetError::QuotaTooSmallForPinnedRoles { .. }
1220                        | BudgetError::Oversubscribed { .. }
1221                        | BudgetError::TooFewSolverCpus { .. }
1222                        // FF-T2: the plan-tier refusal classes.
1223                        // The derive itself never produces them (the plan
1224                        // does); listed so the match stays exhaustive and
1225                        // a future arm is still a compile error.
1226                        | BudgetError::BelowHostFloor { .. }
1227                        | BudgetError::IoWorkersOutOfBounds { .. },
1228                    ) => {
1229                        // Exhaustive typed refusal classes; the match arms
1230                        // above make any NEW error variant a compile error.
1231                    }
1232                }
1233            }
1234        }
1235    }
1236}