use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use crate::core::agent::Delegation;
use crate::core::builder_slot_pool::{SeedKind, SlotPool, SlotReservation};
use crate::core::dispatch_isolation::agent_is_builder;
use crate::core::session::SessionId;
use super::core::DaemonState;
pub const BUILDER_LEASE_TTL_SECS: i64 = 45 * 60;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BuilderHolder {
pub agent: String,
pub session: SessionId,
pub elapsed_secs: i64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BuilderLease {
Held(i64),
ReleasedByStatus,
ReleasedByDeadOwner,
ReleasedByTtl,
}
impl BuilderLease {
#[must_use]
pub fn is_held(self) -> bool {
matches!(self, Self::Held(_))
}
}
#[must_use]
pub fn builder_lease(
delegation: &Delegation,
owner_alive: Option<bool>,
now: chrono::DateTime<chrono::Utc>,
) -> BuilderLease {
if !delegation.status.is_live() {
return BuilderLease::ReleasedByStatus;
}
if owner_alive == Some(false) {
return BuilderLease::ReleasedByDeadOwner;
}
let started = delegation.started_at.unwrap_or(delegation.created_at);
let elapsed = (now - started).num_seconds().max(0);
if elapsed >= BUILDER_LEASE_TTL_SECS {
return BuilderLease::ReleasedByTtl;
}
BuilderLease::Held(elapsed)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BuilderSlotCensus {
pub holders: Vec<BuilderHolder>,
pub expired: Vec<BuilderHolder>,
pub cap: u32,
}
#[derive(Debug, Default)]
pub struct BuilderSlotGrant {
pub holders: Vec<BuilderHolder>,
pub claimed: bool,
pub slot_dir: Option<PathBuf>,
pub slot_seed: Option<String>,
pub slot_notice: Option<String>,
pub slot_refused: Option<String>,
pub seed_index: Option<u32>,
}
impl DaemonState {
#[must_use]
pub fn builder_slot_holders(&self, exclude_tool_use_id: Option<&str>) -> Vec<BuilderHolder> {
self.builder_leases(exclude_tool_use_id, BuilderLease::is_held)
}
#[must_use]
pub fn builder_slot_census(&self, cap: u32) -> BuilderSlotCensus {
BuilderSlotCensus {
holders: self.builder_slot_holders(None),
expired: self.builder_leases(None, |lease| lease == BuilderLease::ReleasedByTtl),
cap,
}
}
#[must_use]
pub fn builder_capacity(
&self,
config: &crate::core::builders::BuildersConfig,
ceiling: u32,
exclude_tool_use_id: Option<&str>,
) -> crate::core::builder_capacity::Capacity {
let held =
u32::try_from(self.builder_slot_holders(exclude_tool_use_id).len()).unwrap_or(u32::MAX);
let readings = crate::core::builder_capacity::sample_capacity_readings();
let mut quiet = self.builder_quiet_window.lock();
crate::core::builder_capacity::resolve_capacity(
&readings,
config,
ceiling,
held,
&mut quiet,
chrono::Utc::now(),
)
}
pub fn claim_builder_slot<C: FnOnce(&Self), R: FnOnce(&Self)>(
&self,
cap: u32,
exclude_tool_use_id: Option<&str>,
eligible: bool,
record: C,
release: R,
) -> (Vec<BuilderHolder>, bool) {
let grant = self.claim_builder_slot_with_pool(
cap,
exclude_tool_use_id,
eligible,
None,
record,
release,
);
(grant.holders, grant.claimed)
}
#[allow(clippy::too_many_arguments)]
pub fn claim_builder_slot_with_pool<C: FnOnce(&Self), R: FnOnce(&Self)>(
&self,
cap: u32,
exclude_tool_use_id: Option<&str>,
eligible: bool,
pool: Option<&SlotPool>,
record: C,
release: R,
) -> BuilderSlotGrant {
let _claim = self.builder_claim_guard();
let holders = self.builder_slot_holders(exclude_tool_use_id);
let admitted = eligible && u32::try_from(holders.len()).unwrap_or(u32::MAX) < cap;
let mut grant = BuilderSlotGrant {
holders,
claimed: admitted,
..BuilderSlotGrant::default()
};
if admitted {
record(self);
let index = self.assign_builder_slot(exclude_tool_use_id);
if let Some(pool) = pool {
match index {
Some(index) => {
self.reserve_slot_dir(pool, index, exclude_tool_use_id, &mut grant);
}
None => {
tracing::warn!(
"an admitted builder could not be assigned a slot index, so it \
builds in the shared target directory"
);
grant.slot_notice = Some(
"no builder slot index could be assigned to this dispatch — its \
delegation record was not found, so no private cargo target \
directory was reserved."
.to_string(),
);
}
}
}
if !grant.claimed {
release(self);
}
} else if eligible {
release(self);
}
grant
}
fn reserve_slot_dir(
&self,
pool: &SlotPool,
index: u32,
exclude_tool_use_id: Option<&str>,
grant: &mut BuilderSlotGrant,
) {
match pool.reserve_path(index) {
Ok(SlotReservation::Ready(path)) => {
let rendered = format!("{:?}", SeedKind::AlreadySeeded);
self.record_builder_slot_dir(exclude_tool_use_id, &path, &rendered);
grant.slot_dir = Some(path);
grant.slot_seed = Some(rendered);
}
Ok(SlotReservation::Seeding(path)) => {
tracing::warn!(
"builder slot {} is not seeded yet, so this dispatch builds in the shared \
target directory while the seed runs",
path.display()
);
if self.builder_seeding.lock().insert(index) {
grant.seed_index = Some(index);
}
grant.slot_notice = Some(format!(
"builder slot {} had not been seeded yet, so this dispatch was admitted \
WITHOUT a private cargo target directory — seeding it inline would have \
outrun the dispatch guard's claim budget. The seed is running; the next \
builder on this slot gets the private directory.",
path.display()
));
}
Err(err) => {
tracing::warn!(
"builder slot {index} could not be reserved, so no slot is granted: {err}"
);
self.clear_builder_slot(exclude_tool_use_id);
grant.claimed = false;
grant.slot_refused = Some(err.to_string());
}
}
}
pub fn finish_builder_seed(&self, index: u32) {
self.builder_seeding.lock().remove(&index);
}
fn record_builder_slot_dir(&self, tool_use_id: Option<&str>, path: &Path, seed: &str) {
let Some(tool_use_id) = tool_use_id else {
return;
};
let _record = self.dispatch_record_guard();
for mut entry in self.delegations.iter_mut() {
if entry.value().tool_use_id.as_deref() == Some(tool_use_id) {
entry.value_mut().builder_slot_dir = Some(path.to_path_buf());
entry.value_mut().builder_slot_seed = Some(seed.to_string());
break;
}
}
}
fn clear_builder_slot(&self, tool_use_id: Option<&str>) {
let Some(tool_use_id) = tool_use_id else {
return;
};
let _record = self.dispatch_record_guard();
for mut entry in self.delegations.iter_mut() {
if entry.value().tool_use_id.as_deref() == Some(tool_use_id) {
entry.value_mut().builder_slot = None;
break;
}
}
}
pub fn release_denied_builder_dispatch(
&self,
session: SessionId,
tool_use_id: Option<&str>,
) -> bool {
let Some(tool_use_id) = tool_use_id else {
return false;
};
let _record = self.dispatch_record_guard();
let Some(id) =
self.find_delegation(session, |d| d.tool_use_id.as_deref() == Some(tool_use_id))
else {
return false;
};
self.terminate_delegation(id, crate::core::agent::DelegationStatus::Cancelled)
}
fn assign_builder_slot(&self, tool_use_id: Option<&str>) -> Option<u32> {
let tool_use_id = tool_use_id?;
let now = chrono::Utc::now();
let taken: std::collections::BTreeSet<u32> = self
.delegations
.iter()
.filter_map(|entry| {
let d = entry.value();
if d.tool_use_id.as_deref() == Some(tool_use_id) || !agent_is_builder(&d.agent) {
return None;
}
builder_lease(d, self.session_owner_alive(d.session), now)
.is_held()
.then_some(d.builder_slot)
.flatten()
})
.collect();
let index = (0u32..).find(|i| !taken.contains(i))?;
let _record = self.dispatch_record_guard();
let mut assigned = None;
for mut entry in self.delegations.iter_mut() {
if entry.value().tool_use_id.as_deref() == Some(tool_use_id) {
entry.value_mut().builder_slot = Some(index);
assigned = Some(index);
break;
}
}
assigned
}
fn builder_leases(
&self,
exclude_tool_use_id: Option<&str>,
keep: impl Fn(BuilderLease) -> bool,
) -> Vec<BuilderHolder> {
let now = chrono::Utc::now();
let mut rows: Vec<BuilderHolder> = self
.delegations
.iter()
.filter_map(|entry| {
let d = entry.value();
if !agent_is_builder(&d.agent) {
return None;
}
if exclude_tool_use_id.is_some() && d.tool_use_id.as_deref() == exclude_tool_use_id
{
return None;
}
let lease = builder_lease(d, self.session_owner_alive(d.session), now);
if !keep(lease) {
return None;
}
let started = d.started_at.unwrap_or(d.created_at);
Some(BuilderHolder {
agent: d.agent.clone(),
session: d.session,
elapsed_secs: (now - started).num_seconds().max(0),
})
})
.collect();
rows.sort_by(|a, b| {
b.elapsed_secs
.cmp(&a.elapsed_secs)
.then_with(|| a.agent.cmp(&b.agent))
});
rows
}
fn session_owner_alive(&self, session: SessionId) -> Option<bool> {
let pid = self.session(session)?.pid?;
Some(crate::core::process::is_process_alive(pid))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::agent::{Delegation, DelegationStatus, ModelTier};
use crate::core::session::{ControlModel, Session, SessionStatus};
fn session_with_pid(state: &DaemonState, pid: Option<u32>) -> SessionId {
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(0);
let n = COUNTER.fetch_add(1, Ordering::Relaxed);
let mut s = Session::new(SessionId::new(), "/tmp/p", ControlModel::Tmux, None);
s.tmux_name = format!("tmpm-builder-test-{n}");
s.status = SessionStatus::Active;
s.pid = pid;
let id = s.id;
state.register_session(s);
id
}
fn running(session: SessionId, agent: &str, age_secs: i64) -> Delegation {
let mut d = Delegation::new(session, None, agent, ModelTier::Sonnet, "build it");
d.status = DelegationStatus::Running;
d.started_at = Some(chrono::Utc::now() - chrono::Duration::seconds(age_secs));
d
}
#[test]
fn a_terminal_delegation_holds_nothing() {
let mut d = running(SessionId::new(), "rust-engineer", 10);
for status in [
DelegationStatus::Completed,
DelegationStatus::Failed,
DelegationStatus::Cancelled,
DelegationStatus::Stale,
] {
d.status = status;
assert_eq!(
builder_lease(&d, Some(true), chrono::Utc::now()),
BuilderLease::ReleasedByStatus,
"{status:?} must release the slot"
);
}
}
#[test]
fn a_lease_is_released_when_the_owner_pid_is_dead() {
let d = running(SessionId::new(), "rust-engineer", 30);
assert_eq!(
builder_lease(&d, Some(false), chrono::Utc::now()),
BuilderLease::ReleasedByDeadOwner,
"a dead dispatcher releases its builders' slots long before the TTL"
);
}
#[test]
fn a_lease_is_released_at_the_ttl_when_the_pid_is_inconclusive() {
let d = running(
SessionId::new(),
"rust-engineer",
BUILDER_LEASE_TTL_SECS + 1,
);
assert_eq!(
builder_lease(&d, None, chrono::Utc::now()),
BuilderLease::ReleasedByTtl
);
let d = running(SessionId::new(), "rust-engineer", BUILDER_LEASE_TTL_SECS);
assert_eq!(
builder_lease(&d, None, chrono::Utc::now()),
BuilderLease::ReleasedByTtl
);
}
#[test]
fn a_lease_inside_the_ttl_is_still_held() {
let d = running(
SessionId::new(),
"rust-engineer",
BUILDER_LEASE_TTL_SECS - 60,
);
let lease = builder_lease(&d, Some(true), chrono::Utc::now());
assert!(lease.is_held(), "{lease:?}");
assert!(matches!(lease, BuilderLease::Held(e) if e >= BUILDER_LEASE_TTL_SECS - 61));
}
#[test]
fn an_unknown_owner_pid_does_not_release_the_lease() {
let d = running(SessionId::new(), "rust-engineer", 30);
assert!(builder_lease(&d, None, chrono::Utc::now()).is_held());
}
#[test]
fn builder_holders_report_the_agent_session_and_elapsed_time() {
let state = DaemonState::new();
let session = session_with_pid(&state, Some(std::process::id()));
state.upsert_delegation(running(session, "rust-engineer", 120));
let holders = state.builder_slot_holders(None);
assert_eq!(holders.len(), 1);
assert_eq!(holders[0].agent, "rust-engineer");
assert_eq!(holders[0].session, session);
assert!(holders[0].elapsed_secs >= 120, "{:?}", holders[0]);
}
#[test]
fn non_builder_delegations_hold_no_slot() {
let state = DaemonState::new();
let session = session_with_pid(&state, Some(std::process::id()));
for agent in [
"research",
"ticketing",
"qa",
"documentation",
"version-control",
] {
state.upsert_delegation(running(session, agent, 60));
}
assert!(state.builder_slot_holders(None).is_empty());
}
#[test]
fn a_local_ops_delegation_holds_a_builder_slot() {
let state = DaemonState::new();
let session = session_with_pid(&state, Some(std::process::id()));
state.upsert_delegation(running(session, "local-ops", 5));
assert_eq!(state.builder_slot_holders(None).len(), 1);
}
#[test]
fn a_dead_sessions_builders_stop_holding_slots() {
let state = DaemonState::new();
let dead = session_with_pid(&state, Some(u32::MAX));
let alive = session_with_pid(&state, Some(std::process::id()));
state.upsert_delegation(running(dead, "rust-engineer", 30));
state.upsert_delegation(running(alive, "python-engineer", 30));
let holders = state.builder_slot_holders(None);
assert_eq!(holders.len(), 1, "{holders:?}");
assert_eq!(holders[0].agent, "python-engineer");
}
#[test]
fn builder_cap_admits_up_to_the_cap_and_denies_the_rest() {
let state = DaemonState::new();
let a = session_with_pid(&state, Some(std::process::id()));
let b = session_with_pid(&state, Some(std::process::id()));
let mut admitted = 0;
let mut last_holders = Vec::new();
for (session, agent) in [
(a, "rust-engineer"),
(b, "python-engineer"),
(a, "react-engineer"),
(b, "local-ops"),
] {
let (holders, claimed) = state.claim_builder_slot(
2,
None,
true,
|s| s.upsert_delegation(running(session, agent, 1)),
|_| {},
);
last_holders = holders;
if claimed {
admitted += 1;
}
}
assert_eq!(admitted, 2, "the cap is machine-wide, not per session");
assert_eq!(
last_holders.len(),
2,
"the deny must name both actual holders: {last_holders:?}"
);
let names: Vec<&str> = last_holders.iter().map(|h| h.agent.as_str()).collect();
assert!(names.contains(&"rust-engineer"), "{names:?}");
assert!(names.contains(&"python-engineer"), "{names:?}");
}
#[test]
fn builder_cap_admits_exactly_one_of_two_simultaneous_claims() {
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
let state = Arc::new(DaemonState::new());
let session = session_with_pid(&state, Some(std::process::id()));
let admitted = Arc::new(AtomicUsize::new(0));
let barrier = Arc::new(std::sync::Barrier::new(2));
let handles: Vec<_> = ["rust-engineer", "python-engineer"]
.into_iter()
.map(|agent| {
let state = Arc::clone(&state);
let admitted = Arc::clone(&admitted);
let barrier = Arc::clone(&barrier);
std::thread::spawn(move || {
barrier.wait();
let (_, claimed) = state.claim_builder_slot(
1,
None,
true,
|s| s.upsert_delegation(running(session, agent, 0)),
|_| {},
);
if claimed {
admitted.fetch_add(1, Ordering::Relaxed);
}
})
})
.collect();
for h in handles {
h.join().expect("thread");
}
assert_eq!(
admitted.load(Ordering::Relaxed),
1,
"one free slot must admit exactly one of two simultaneous dispatches"
);
assert_eq!(state.builder_slot_holders(None).len(), 1);
}
#[test]
fn an_admitted_builder_is_assigned_the_lowest_free_slot() {
let state = DaemonState::new();
let session = session_with_pid(&state, Some(std::process::id()));
for (n, expected) in [("toolu_A", 0), ("toolu_B", 1), ("toolu_C", 2)] {
let mut d = running(session, "rust-engineer", 1);
d.tool_use_id = Some(n.to_string());
let (_, claimed) = state.claim_builder_slot(
4,
Some(n),
true,
|s| s.upsert_delegation(d.clone()),
|_| {},
);
assert!(claimed, "{n} must be admitted under a ceiling of 4");
let got = state
.delegations
.iter()
.find(|e| e.value().tool_use_id.as_deref() == Some(n))
.and_then(|e| e.value().builder_slot);
assert_eq!(got, Some(expected), "{n} took the wrong slot");
}
}
fn test_pool(root: std::path::PathBuf) -> SlotPool {
SlotPool::new(
root,
trusty_common::github_path::GithubPath {
owner: "acme".to_string(),
repo: "widgets".to_string(),
},
)
}
#[test]
fn an_admitted_builder_records_the_slot_directory_it_was_given() {
let root = tempfile::tempdir().expect("tempdir");
let state = DaemonState::new();
let session = session_with_pid(&state, Some(std::process::id()));
let pool = test_pool(root.path().join("pool"));
pool.seed(0, None).expect("a seeded slot 0");
let mut d = running(session, "rust-engineer", 1);
d.tool_use_id = Some("toolu_A".to_string());
let grant = state.claim_builder_slot_with_pool(
4,
Some("toolu_A"),
true,
Some(&pool),
|s| s.upsert_delegation(d.clone()),
|_| {},
);
assert!(grant.claimed, "a quiet machine with a usable pool admits");
let dir = grant
.slot_dir
.expect("an admitted builder gets a directory");
assert!(dir.is_dir(), "the slot directory must exist: {dir:?}");
assert!(
dir.ends_with("slot-0"),
"the first builder takes slot-0: {dir:?}"
);
let recorded = state
.delegations
.iter()
.find(|e| e.value().tool_use_id.as_deref() == Some("toolu_A"))
.map(|e| e.value().builder_slot_dir.clone());
assert_eq!(
recorded,
Some(Some(dir)),
"the lease must carry the directory, or nothing can report it"
);
}
#[test]
fn an_unseeded_slot_admits_with_no_directory_and_seeds_nothing_inline() {
let root = tempfile::tempdir().expect("tempdir");
let state = DaemonState::new();
let session = session_with_pid(&state, Some(std::process::id()));
let pool = test_pool(root.path().join("pool"));
let mut d = running(session, "rust-engineer", 1);
d.tool_use_id = Some("toolu_A".to_string());
let grant = state.claim_builder_slot_with_pool(
4,
Some("toolu_A"),
true,
Some(&pool),
|s| s.upsert_delegation(d.clone()),
|_| {},
);
assert!(
grant.claimed,
"an unseeded slot admits — it is the SEED that is deferred, not the dispatch"
);
assert!(
grant.slot_dir.is_none(),
"a slot whose seed has not run is not handed out: {:?}",
grant.slot_dir
);
assert_eq!(
grant.seed_index,
Some(0),
"the caller is told which slot to seed off the claim path"
);
assert!(
grant.slot_notice.is_some(),
"admitting with no private directory must say so"
);
assert!(
!pool.slot_path(0).exists(),
"the reservation creates the parent only, never the slot itself"
);
}
#[test]
fn a_slot_the_pool_cannot_provide_is_refused_not_admitted_unthrottled() {
let root = tempfile::tempdir().expect("tempdir");
let blocker = root.path().join("not-a-directory");
std::fs::write(&blocker, b"#8261").expect("write blocker");
let state = DaemonState::new();
let session = session_with_pid(&state, Some(std::process::id()));
let pool = test_pool(blocker);
let mut d = running(session, "rust-engineer", 1);
d.tool_use_id = Some("toolu_A".to_string());
let mut released = false;
let grant = state.claim_builder_slot_with_pool(
4,
Some("toolu_A"),
true,
Some(&pool),
|s| s.upsert_delegation(d.clone()),
|_| released = true,
);
assert!(
!grant.claimed,
"an unprovidable slot must refuse the claim, never admit unthrottled"
);
assert!(
grant.slot_dir.is_none(),
"a refused claim carries no directory"
);
assert!(
released,
"the refusal must release the record the guard already wrote"
);
assert!(
grant
.slot_refused
.as_deref()
.is_some_and(|d| d.contains("not-a-directory")),
"a pool refusal names the path it could not make: {:?}",
grant.slot_refused
);
let index = state
.delegations
.iter()
.find(|e| e.value().tool_use_id.as_deref() == Some("toolu_A"))
.and_then(|e| e.value().builder_slot);
assert_eq!(
index, None,
"the index must be cleared, or it counts against the next builder"
);
}
#[test]
fn a_released_slot_index_is_reassigned_to_the_next_builder() {
let state = DaemonState::new();
let session = session_with_pid(&state, Some(std::process::id()));
let mut first = running(session, "rust-engineer", 1);
first.tool_use_id = Some("toolu_1".to_string());
let first_id = first.id;
state.claim_builder_slot(
2,
Some("toolu_1"),
true,
|s| s.upsert_delegation(first.clone()),
|_| {},
);
state.terminate_delegation(first_id, DelegationStatus::Completed);
let mut second = running(session, "rust-engineer", 1);
second.tool_use_id = Some("toolu_2".to_string());
let (_, claimed) = state.claim_builder_slot(
2,
Some("toolu_2"),
true,
|s| s.upsert_delegation(second.clone()),
|_| {},
);
assert!(claimed);
let got = state
.delegations
.iter()
.find(|e| e.value().tool_use_id.as_deref() == Some("toolu_2"))
.and_then(|e| e.value().builder_slot);
assert_eq!(
got,
Some(0),
"the freed index is reused, not skipped for a cold one"
);
}
#[test]
fn a_refused_builder_claim_records_nothing() {
let state = DaemonState::new();
let session = session_with_pid(&state, Some(std::process::id()));
state.upsert_delegation(running(session, "rust-engineer", 10));
let mut recorded = false;
let mut released = false;
let (holders, claimed) =
state.claim_builder_slot(1, None, true, |_| recorded = true, |_| released = true);
assert_eq!(holders.len(), 1, "the deny must name the holder");
assert!(!claimed);
assert!(
!recorded,
"nothing may be written when the claim is refused"
);
assert!(released, "a refused eligible claim must release");
}
#[test]
fn a_non_builder_dispatch_claims_no_slot() {
let state = DaemonState::new();
let mut recorded = false;
let mut released = false;
let (holders, claimed) =
state.claim_builder_slot(4, None, false, |_| recorded = true, |_| released = true);
assert!(holders.is_empty());
assert!(!claimed);
assert!(!recorded);
assert!(!released, "an ineligible payload must not be released");
}
#[test]
fn a_builder_claim_excludes_the_callers_own_dispatch() {
let state = DaemonState::new();
let session = session_with_pid(&state, Some(std::process::id()));
let mut mine = running(session, "rust-engineer", 0);
mine.tool_use_id = Some("toolu_MINE".to_string());
state.upsert_delegation(mine);
let (holders, claimed) =
state.claim_builder_slot(1, Some("toolu_MINE"), true, |_| {}, |_| {});
assert!(
holders.is_empty() && claimed,
"a dispatch must never be denied by its own record: {holders:?}"
);
}
#[test]
fn releasing_an_unknown_dispatch_is_a_no_op() {
let state = DaemonState::new();
let session = session_with_pid(&state, Some(std::process::id()));
state.upsert_delegation(running(session, "rust-engineer", 10));
assert!(!state.release_denied_builder_dispatch(session, None));
assert!(!state.release_denied_builder_dispatch(session, Some("toolu_NOT_HERE")));
assert_eq!(
state.builder_slot_holders(None).len(),
1,
"a no-op release must not touch the live holder"
);
}
#[test]
fn census_separates_holders_from_expired_leases() {
let state = DaemonState::new();
let session = session_with_pid(&state, None);
state.upsert_delegation(running(session, "rust-engineer", 60));
state.upsert_delegation(running(
session,
"python-engineer",
BUILDER_LEASE_TTL_SECS + 60,
));
let census = state.builder_slot_census(3);
assert_eq!(census.cap, 3);
assert_eq!(census.holders.len(), 1);
assert_eq!(census.holders[0].agent, "rust-engineer");
assert_eq!(census.expired.len(), 1);
assert_eq!(census.expired[0].agent, "python-engineer");
}
}