use std::sync::Arc;
use axum::{Json, Router, extract::Path, extract::State, routing::get, routing::post};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::core::agent::is_subagent_dispatch_tool;
use crate::core::builder_capacity::Capacity;
#[cfg(test)]
use crate::core::builder_capacity::CapacityReason;
use crate::core::builder_slot_pool::SlotPool;
use crate::core::builders::resolve_max_concurrent;
use crate::core::config::MpmConfig;
use crate::core::dispatch_isolation::{agent_is_builder, dispatch_agent};
use crate::core::hook::HookEvent;
use crate::core::session::SessionId;
use crate::daemon::error::DaemonError;
use crate::daemon::state::{BuilderHolder, BuilderSlotCensus, DaemonState};
#[derive(Debug, Deserialize)]
pub struct BuilderSlotRequest {
pub payload: Value,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct BuilderSlotResponse {
pub holders: Vec<BuilderHolder>,
pub cap: u32,
#[serde(default)]
pub claimed: bool,
#[serde(default)]
pub ineligible: bool,
#[serde(default)]
pub ceiling: u32,
#[serde(default)]
pub capacity_reason: String,
#[serde(default)]
pub fail_closed_surface: Option<String>,
#[serde(default)]
pub slot_path: Option<String>,
#[serde(default)]
pub slot_seed: Option<String>,
#[serde(default)]
pub slot_notice: Option<String>,
#[serde(default)]
pub slot_refused: Option<String>,
}
pub struct SlotClaimOutcome {
pub response: BuilderSlotResponse,
pub seed_index: Option<u32>,
}
pub fn router() -> Router<Arc<DaemonState>> {
Router::new()
.route(
"/api/v1/sessions/{id}/delegations/builder-slot",
post(builder_slot_route),
)
.route("/api/v1/builder-slots", get(builder_slot_census_route))
}
pub async fn builder_slot_route(
State(state): State<Arc<DaemonState>>,
Path(id): Path<String>,
Json(req): Json<BuilderSlotRequest>,
) -> Result<Json<BuilderSlotResponse>, DaemonError> {
let config = MpmConfig::load_default().builders;
let capacity = capacity_for(&state, &config, resolve_max_concurrent(), &req.payload);
let project_dir = str_field(&req.payload, "cwd").map(std::path::PathBuf::from);
let resolved = resolve_slot_pool(&config, dirs::home_dir(), project_dir.as_deref());
let outcome = builder_slot_op_with_pool(
&state,
&id,
req,
&capacity,
resolved.pool.as_ref(),
resolved.notice.as_deref(),
)?;
if let (Some(index), Some(pool)) = (outcome.seed_index, resolved.pool) {
spawn_seed(Arc::clone(&state), pool, resolved.clone_from, index);
}
Ok(Json(outcome.response))
}
fn spawn_seed(
state: Arc<DaemonState>,
pool: SlotPool,
clone_from: Option<std::path::PathBuf>,
index: u32,
) -> tokio::task::JoinHandle<()> {
tokio::task::spawn_blocking(move || {
let _guard = SeedGuard { state, index };
if let Err(err) = pool.seed(index, clone_from.as_deref()) {
tracing::warn!("builder slot {index} could not be seeded: {err}");
}
})
}
struct SeedGuard {
state: Arc<DaemonState>,
index: u32,
}
impl Drop for SeedGuard {
fn drop(&mut self) {
self.state.finish_builder_seed(self.index);
}
}
struct ResolvedPool {
pool: Option<SlotPool>,
clone_from: Option<std::path::PathBuf>,
notice: Option<String>,
}
fn capacity_for(
state: &DaemonState,
config: &crate::core::builders::BuildersConfig,
ceiling: u32,
payload: &Value,
) -> Capacity {
state.builder_capacity(config, ceiling, str_field(payload, "tool_use_id"))
}
fn resolve_slot_pool(
config: &crate::core::builders::BuildersConfig,
home: Option<std::path::PathBuf>,
project_dir: Option<&std::path::Path>,
) -> ResolvedPool {
let no_pool = |why: &str| {
tracing::warn!("no builder slot pool for this dispatch: {why}");
ResolvedPool {
pool: None,
clone_from: None,
notice: Some(format!(
"no private cargo target directory was reserved for this dispatch: {why}. It \
builds in the shared target directory and contends on its lock."
)),
}
};
let Some(home) = home else {
return no_pool("this daemon cannot resolve a home directory, so the pool root is unknown");
};
let Some(project_dir) = project_dir else {
return no_pool("the dispatch payload named no `cwd`");
};
let Some(identity) = trusty_common::github_path::derive_github_path(project_dir) else {
return no_pool("the checkout has no git origin identity to key a pool by");
};
let build = trusty_common::crate_config::load_at::<
crate::core::trusty_tools_config::TrustyToolsConfig,
>(&trusty_common::crate_config::crate_config_path_at(
&home,
crate::core::trusty_tools_config::CRATE_NAME,
))
.ok()
.flatten();
let clone_from = crate::core::build_env::resolve_build_env(
build.as_ref().and_then(|c| c.build.as_ref()),
&home,
Some(&identity),
crate::core::build_env::host_cores(),
)
.ok()
.map(|env| env.cargo_target_dir);
ResolvedPool {
pool: Some(SlotPool::new(
config.effective_slot_pool_root(&home),
identity,
)),
clone_from,
notice: None,
}
}
pub fn builder_slot_op(
state: &Arc<DaemonState>,
id: &str,
req: BuilderSlotRequest,
cap: u32,
) -> Result<BuilderSlotResponse, DaemonError> {
let session = uuid::Uuid::parse_str(id)
.map(SessionId)
.map_err(|_| DaemonError::InvalidRequest(format!("malformed session id: {id}")))?;
let payload = &req.payload;
let exclude = str_field(payload, "tool_use_id");
let input = payload.get("input");
let eligible = exclude.is_some()
&& str_field(payload, "tool").is_some_and(is_subagent_dispatch_tool)
&& dispatch_agent(input).is_some_and(agent_is_builder);
let (holders, claimed) = state.claim_builder_slot(
cap,
exclude,
eligible,
|s| {
crate::daemon::services::delegation_tracker::observe(
s,
session,
HookEvent::PreToolUse,
payload,
);
},
|s| {
crate::daemon::services::delegation_tracker::release_denied_dispatch(
s, session, payload,
);
},
);
Ok(BuilderSlotResponse {
holders,
cap,
claimed,
ineligible: !eligible,
ceiling: cap,
capacity_reason: String::new(),
fail_closed_surface: None,
slot_path: None,
slot_seed: None,
slot_notice: None,
slot_refused: None,
})
}
pub fn builder_slot_op_with_pool(
state: &Arc<DaemonState>,
id: &str,
req: BuilderSlotRequest,
capacity: &Capacity,
pool: Option<&SlotPool>,
no_pool_notice: Option<&str>,
) -> Result<SlotClaimOutcome, DaemonError> {
let session = uuid::Uuid::parse_str(id)
.map(SessionId)
.map_err(|_| DaemonError::InvalidRequest(format!("malformed session id: {id}")))?;
let payload = &req.payload;
let exclude = str_field(payload, "tool_use_id");
let input = payload.get("input");
let eligible = exclude.is_some()
&& str_field(payload, "tool").is_some_and(is_subagent_dispatch_tool)
&& dispatch_agent(input).is_some_and(agent_is_builder);
let grant = state.claim_builder_slot_with_pool(
capacity.n_effective,
exclude,
eligible,
pool,
|s| {
crate::daemon::services::delegation_tracker::observe(
s,
session,
HookEvent::PreToolUse,
payload,
);
},
|s| {
crate::daemon::services::delegation_tracker::release_denied_dispatch(
s, session, payload,
);
},
);
let claimed = grant.claimed;
let slot_notice = grant.slot_notice.or_else(|| {
claimed
.then(|| no_pool_notice.map(str::to_string))
.flatten()
});
Ok(SlotClaimOutcome {
response: BuilderSlotResponse {
holders: grant.holders,
cap: capacity.n_effective,
claimed: grant.claimed,
ineligible: !eligible,
ceiling: capacity.ceiling,
capacity_reason: capacity.reason.to_string(),
fail_closed_surface: capacity
.reason
.fail_closed_surface()
.map(|surface| surface.name().to_string()),
slot_path: grant.slot_dir.map(|dir| dir.to_string_lossy().into_owned()),
slot_seed: grant.slot_seed,
slot_notice,
slot_refused: grant.slot_refused,
},
seed_index: grant.seed_index,
})
}
pub fn builder_slot_op_with_capacity(
state: &Arc<DaemonState>,
id: &str,
req: BuilderSlotRequest,
capacity: &Capacity,
) -> Result<BuilderSlotResponse, DaemonError> {
let mut response = builder_slot_op(state, id, req, capacity.n_effective)?;
response.ceiling = capacity.ceiling;
response.capacity_reason = capacity.reason.to_string();
response.fail_closed_surface = capacity
.reason
.fail_closed_surface()
.map(|surface| surface.name().to_string());
Ok(response)
}
pub async fn builder_slot_census_route(
State(state): State<Arc<DaemonState>>,
) -> Json<BuilderSlotCensus> {
Json(state.builder_slot_census(resolve_max_concurrent()))
}
fn str_field<'a>(payload: &'a Value, key: &str) -> Option<&'a str> {
payload
.get(key)
.and_then(Value::as_str)
.filter(|s| !s.is_empty())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::agent::{Delegation, DelegationStatus, ModelTier};
use crate::core::paths::FrameworkPaths;
fn hermetic() -> (Arc<DaemonState>, tempfile::TempDir, SessionId) {
let dir = tempfile::tempdir().expect("temp dir");
let paths = FrameworkPaths::under(dir.path());
let state = Arc::new(DaemonState::with_paths(&paths));
(state, dir, SessionId(uuid::Uuid::new_v4()))
}
fn dispatch(agent: &str, tool_use_id: Option<&str>) -> BuilderSlotRequest {
let mut payload = serde_json::json!({
"cwd": "/repo",
"tool": "Agent",
"input": {"subagent_type": agent, "description": "go"},
});
if let Some(id) = tool_use_id {
payload["tool_use_id"] = Value::String(id.to_string());
}
BuilderSlotRequest { payload }
}
fn insert_builder(state: &DaemonState, session: SessionId, agent: &str) {
let mut d = Delegation::new(session, None, agent, ModelTier::Sonnet, "build");
d.status = DelegationStatus::Running;
d.started_at = Some(chrono::Utc::now());
state.upsert_delegation(d);
}
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 the_route_grants_a_private_target_dir_from_the_pool() {
let (state, dir, session) = hermetic();
let pool = test_pool(dir.path().join("pool"));
pool.seed(0, None).expect("a seeded slot 0");
let body = builder_slot_op_with_pool(
&state,
&session.0.to_string(),
dispatch("rust-engineer", Some("toolu_A")),
&capacity(
4,
4,
CapacityReason::WaitingOutQuietWindow { remaining_secs: 0 },
),
Some(&pool),
None,
)
.expect("a well-formed session id")
.response;
assert!(body.claimed, "a quiet machine admits the first builder");
let path = body
.slot_path
.expect("an admitted builder gets a directory");
assert!(path.ends_with("slot-0"), "{path}");
assert!(
std::path::Path::new(&path).is_dir(),
"the directory must exist: {path}"
);
assert!(
body.slot_seed.is_some(),
"the answer says how it was seeded"
);
}
#[test]
fn a_pool_that_cannot_provide_a_directory_refuses_the_claim() {
let (state, dir, session) = hermetic();
let blocker = dir.path().join("not-a-directory");
std::fs::write(&blocker, b"#8261").expect("write blocker");
let body = builder_slot_op_with_pool(
&state,
&session.0.to_string(),
dispatch("rust-engineer", Some("toolu_A")),
&capacity(
4,
4,
CapacityReason::WaitingOutQuietWindow { remaining_secs: 0 },
),
Some(&test_pool(blocker)),
None,
)
.expect("a well-formed session id")
.response;
assert!(
!body.claimed,
"an unprovidable slot must refuse, not admit unthrottled"
);
assert!(body.slot_path.is_none(), "a refusal carries no directory");
assert!(
!body.ineligible,
"this payload WAS eligible; the pool is what failed"
);
assert!(
body.slot_refused
.as_deref()
.is_some_and(|d| d.contains("not-a-directory")),
"a pool refusal must name itself: {:?}",
body.slot_refused
);
}
#[test]
fn an_unseeded_slot_admits_without_copying_anything_on_the_claim_path() {
let (state, dir, session) = hermetic();
let shared = dir.path().join("shared");
std::fs::create_dir_all(&shared).expect("a warm shared dir");
std::fs::write(shared.join("sentinel.rlib"), b"warm").expect("an artifact");
let pool = test_pool(dir.path().join("pool"));
let outcome = builder_slot_op_with_pool(
&state,
&session.0.to_string(),
dispatch("rust-engineer", Some("toolu_A")),
&capacity(
4,
4,
CapacityReason::WaitingOutQuietWindow { remaining_secs: 0 },
),
Some(&pool),
None,
)
.expect("a well-formed session id");
assert!(
outcome.response.claimed,
"an unseeded slot admits: it is the SEED that is deferred"
);
assert!(
outcome.response.slot_path.is_none(),
"an unseeded slot is not handed out: {:?}",
outcome.response.slot_path
);
assert_eq!(
outcome.seed_index,
Some(0),
"the seed is handed back for the route to run after answering"
);
assert!(
!pool.slot_path(0).join("sentinel.rlib").exists(),
"nothing may be cloned while the hook waits on the claim"
);
assert!(
outcome.response.slot_notice.is_some(),
"admitting with no private directory must say so"
);
}
#[test]
fn a_second_reservation_does_not_spawn_a_second_seed() {
let (state, dir, session) = hermetic();
let pool = test_pool(dir.path().join("pool"));
let first = builder_slot_op_with_pool(
&state,
&session.0.to_string(),
dispatch("rust-engineer", Some("toolu_A")),
&capacity(
4,
4,
CapacityReason::WaitingOutQuietWindow { remaining_secs: 0 },
),
Some(&pool),
None,
)
.expect("a well-formed session id");
assert_eq!(
first.seed_index,
Some(0),
"the first reservation wins the seed"
);
assert!(
state.release_denied_builder_dispatch(session, Some("toolu_A")),
"the first dispatch's record must exist to be ended"
);
let second = builder_slot_op_with_pool(
&state,
&session.0.to_string(),
dispatch("rust-engineer", Some("toolu_B")),
&capacity(
4,
4,
CapacityReason::WaitingOutQuietWindow { remaining_secs: 0 },
),
Some(&pool),
None,
)
.expect("a well-formed session id");
assert!(
second.response.claimed,
"the second dispatch is still admitted — only the SEED is suppressed"
);
assert_eq!(
second.seed_index, None,
"a seed already in flight for this index must not be spawned twice"
);
assert!(
second.response.slot_notice.is_some(),
"and the admission still says it carries no private directory"
);
state.release_denied_builder_dispatch(session, Some("toolu_B"));
state.finish_builder_seed(0);
let third = builder_slot_op_with_pool(
&state,
&session.0.to_string(),
dispatch("rust-engineer", Some("toolu_C")),
&capacity(
4,
4,
CapacityReason::WaitingOutQuietWindow { remaining_secs: 0 },
),
Some(&pool),
None,
)
.expect("a well-formed session id");
assert_eq!(
third.seed_index,
Some(0),
"a released index is seedable again by the next claim to hold it"
);
}
#[tokio::test]
async fn the_route_sequence_seeds_once_then_grants_the_slot_ready() {
let (state, dir, session) = hermetic();
let shared = dir.path().join("shared");
std::fs::create_dir_all(&shared).expect("a warm shared dir");
std::fs::write(shared.join("sentinel.rlib"), b"warm").expect("an artifact");
let pool = test_pool(dir.path().join("pool"));
let first = builder_slot_op_with_pool(
&state,
&session.0.to_string(),
dispatch("rust-engineer", Some("toolu_A")),
&capacity(
4,
4,
CapacityReason::WaitingOutQuietWindow { remaining_secs: 0 },
),
Some(&pool),
None,
)
.expect("a well-formed session id");
assert!(
first.response.slot_path.is_none(),
"the first claim is cold"
);
let index = first.seed_index.expect("the first claim owes a seed");
spawn_seed(
Arc::clone(&state),
pool.clone(),
Some(shared.clone()),
index,
)
.await
.expect("the seed task runs to completion");
state.release_denied_builder_dispatch(session, Some("toolu_A"));
let second = builder_slot_op_with_pool(
&state,
&session.0.to_string(),
dispatch("rust-engineer", Some("toolu_B")),
&capacity(
4,
4,
CapacityReason::WaitingOutQuietWindow { remaining_secs: 0 },
),
Some(&pool),
None,
)
.expect("a well-formed session id")
.response;
let path = second
.slot_path
.expect("the seeded slot is granted to the next builder");
assert!(path.ends_with("slot-0"), "{path}");
assert_eq!(
second.slot_seed.as_deref(),
Some("AlreadySeeded"),
"a seeded slot is granted as warm, not re-seeded"
);
assert!(
second.slot_notice.is_none(),
"a granted slot owes no explanation: {:?}",
second.slot_notice
);
}
#[test]
fn a_panicking_seed_still_releases_its_index() {
let (state, dir, session) = hermetic();
let pool = test_pool(dir.path().join("pool"));
let first = builder_slot_op_with_pool(
&state,
&session.0.to_string(),
dispatch("rust-engineer", Some("toolu_A")),
&capacity(
4,
4,
CapacityReason::WaitingOutQuietWindow { remaining_secs: 0 },
),
Some(&pool),
None,
)
.expect("a well-formed session id");
assert_eq!(first.seed_index, Some(0), "the claim holds index 0's seed");
let hook = std::panic::take_hook();
std::panic::set_hook(Box::new(|_| {}));
let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _guard = SeedGuard {
state: Arc::clone(&state),
index: 0,
};
panic!("the clone died mid-copy");
}));
std::panic::set_hook(hook);
assert!(outcome.is_err(), "the seed panicked, as this test intends");
state.release_denied_builder_dispatch(session, Some("toolu_A"));
let second = builder_slot_op_with_pool(
&state,
&session.0.to_string(),
dispatch("rust-engineer", Some("toolu_B")),
&capacity(
4,
4,
CapacityReason::WaitingOutQuietWindow { remaining_secs: 0 },
),
Some(&pool),
None,
)
.expect("a well-formed session id");
assert_eq!(
second.seed_index,
Some(0),
"a panicked seed must leave the index seedable, not held forever"
);
}
#[test]
fn a_dispatch_with_no_repo_identity_admits_with_a_notice() {
let (state, _dir, session) = hermetic();
let body = builder_slot_op_with_pool(
&state,
&session.0.to_string(),
dispatch("rust-engineer", Some("toolu_A")),
&capacity(
4,
4,
CapacityReason::WaitingOutQuietWindow { remaining_secs: 0 },
),
None,
Some("the checkout has no git origin identity to key a pool by"),
)
.expect("a well-formed session id")
.response;
assert!(body.claimed, "no pool is not a refusal");
assert!(body.slot_path.is_none());
assert!(
body.slot_notice
.as_deref()
.is_some_and(|n| n.contains("no git origin identity")),
"the admission must say why it carries no directory: {:?}",
body.slot_notice
);
}
#[test]
fn the_throttle_excludes_the_claimants_own_record() {
let (state, _dir, session) = hermetic();
insert_builder(&state, session, "rust-engineer");
insert_builder(&state, session, "python-engineer");
let mut mine = Delegation::new(session, None, "rust-engineer", ModelTier::Sonnet, "build");
mine.status = DelegationStatus::Running;
mine.started_at = Some(chrono::Utc::now());
mine.tool_use_id = Some("toolu_SELF".to_string());
state.upsert_delegation(mine);
let config = crate::core::builders::BuildersConfig {
free_memory_floor_mb: Some(1024 * 1024),
..crate::core::builders::BuildersConfig::default()
};
let request = dispatch("rust-engineer", Some("toolu_SELF"));
let measured = capacity_for(&state, &config, 4, &request.payload);
assert_eq!(
measured.n_effective, 2,
"a throttled machine floors N at its REAL holders, not at three: {:?}",
measured.reason
);
let body =
builder_slot_op_with_capacity(&state, &session.0.to_string(), request, &measured)
.expect("a well-formed session id");
assert!(
!body.claimed,
"the third builder on a throttled two-holder machine must be refused"
);
}
fn capacity(n_effective: u32, ceiling: u32, reason: CapacityReason) -> Capacity {
Capacity {
n_effective,
ceiling,
reason,
}
}
#[test]
fn the_route_admits_against_the_measured_count_and_reports_the_ceiling() {
let (state, _dir, session) = hermetic();
insert_builder(&state, session, "rust-engineer");
let measured = capacity(
1,
4,
CapacityReason::LoadAboveThreshold {
load: 40.0,
threshold: 32.0,
},
);
let body = builder_slot_op_with_capacity(
&state,
&session.0.to_string(),
dispatch("python-engineer", Some("toolu_M")),
&measured,
)
.expect("route succeeds");
assert!(
!body.claimed,
"a second builder is refused at a measured count of 1, though the ceiling is 4"
);
assert_eq!(body.cap, 1, "the refusal reports the MEASURED count");
assert_eq!(body.ceiling, 4, "and the configured ceiling beside it");
assert!(
body.capacity_reason.contains("40.00"),
"{}",
body.capacity_reason
);
assert!(
body.capacity_reason.contains("32.00"),
"{}",
body.capacity_reason
);
assert_eq!(
body.fail_closed_surface, None,
"an EXCEEDED limit is not an UNREADABLE one"
);
}
#[test]
fn a_fail_closed_reading_is_named_in_the_answer() {
let (state, _dir, session) = hermetic();
let failed = capacity(
4,
4,
CapacityReason::FailedClosedToCeiling(crate::core::builder_capacity::ReadFailure {
surface: crate::core::builder_capacity::FailClosedSurface::Load,
detail: "operation not permitted".to_string(),
errno: Some(1),
}),
);
let body = builder_slot_op_with_capacity(
&state,
&session.0.to_string(),
dispatch("rust-engineer", Some("toolu_F")),
&failed,
)
.expect("route succeeds");
assert!(
body.claimed,
"fail CLOSED is the fixed ceiling, not zero slots"
);
assert_eq!(body.cap, 4);
assert_eq!(
body.fail_closed_surface.as_deref(),
Some("builder-cap-load-read-failure"),
);
assert!(
body.capacity_reason.contains("errno 1"),
"{}",
body.capacity_reason
);
}
#[test]
fn the_daemon_resolves_capacity_against_its_own_quiet_window() {
let (state, _dir, session) = hermetic();
insert_builder(&state, session, "rust-engineer");
let config = crate::core::builders::BuildersConfig {
max_concurrent: Some(4),
..crate::core::builders::BuildersConfig::default()
};
let capacity = state.builder_capacity(&config, 4, None);
assert_eq!(capacity.ceiling, 4);
assert!(
capacity.n_effective >= 1 && capacity.n_effective <= 4,
"N is clamped into floor..=ceiling whatever this host reads: {capacity:?}"
);
assert!(
!capacity.reason.to_string().is_empty(),
"the number never travels without its reason"
);
}
#[test]
fn builder_slot_route_claims_a_free_slot() {
let (state, _dir, session) = hermetic();
let body = builder_slot_op(
&state,
&session.0.to_string(),
dispatch("rust-engineer", Some("toolu_A")),
2,
)
.expect("route succeeds");
assert!(body.claimed, "an idle machine admits the first builder");
assert_eq!(body.cap, 2);
assert!(body.holders.is_empty());
assert_eq!(state.builder_slot_holders(None).len(), 1);
}
#[test]
fn builder_slot_route_denies_over_the_cap_and_names_the_holders() {
let (state, _dir, session) = hermetic();
insert_builder(&state, session, "rust-engineer");
insert_builder(&state, session, "local-ops");
let body = builder_slot_op(
&state,
&session.0.to_string(),
dispatch("python-engineer", Some("toolu_C")),
2,
)
.expect("route succeeds");
assert!(!body.claimed);
assert_eq!(body.holders.len(), 2);
let names: Vec<&str> = body.holders.iter().map(|h| h.agent.as_str()).collect();
assert!(names.contains(&"rust-engineer"), "{names:?}");
assert!(names.contains(&"local-ops"), "{names:?}");
assert!(body.holders.iter().all(|h| h.session == session));
}
#[test]
fn builder_slot_route_claims_nothing_for_a_non_builder() {
let (state, _dir, session) = hermetic();
for agent in ["research", "ticketing", "documentation", "version-control"] {
let body = builder_slot_op(
&state,
&session.0.to_string(),
dispatch(agent, Some("toolu_X")),
4,
)
.expect("route succeeds");
assert!(!body.claimed, "{agent} must not take a builder slot");
}
assert!(state.builder_slot_holders(None).is_empty());
}
#[test]
fn builder_slot_route_claims_nothing_without_a_tool_use_id() {
let (state, _dir, session) = hermetic();
let body = builder_slot_op(
&state,
&session.0.to_string(),
dispatch("rust-engineer", None),
4,
)
.expect("route succeeds");
assert!(!body.claimed);
assert!(state.builder_slot_holders(None).is_empty());
}
#[test]
fn builder_slot_route_rejects_a_malformed_session_id() {
let (state, _dir, _session) = hermetic();
let err = builder_slot_op(
&state,
"not-a-uuid",
dispatch("rust-engineer", Some("toolu_A")),
2,
)
.expect_err("a malformed id is a 400");
assert!(matches!(err, DaemonError::InvalidRequest(_)));
}
fn record_the_preceding_claim_wrote(
state: &DaemonState,
session: SessionId,
payload: &Value,
granted: bool,
) {
if granted {
let mut isolated = payload.clone();
isolated["input"]["isolation"] = Value::String("worktree".to_string());
crate::daemon::services::delegation_tracker::record_granted_isolation(
state, session, &isolated,
);
} else {
crate::daemon::services::delegation_tracker::observe(
state,
session,
HookEvent::PreToolUse,
payload,
);
}
}
#[test]
fn a_denied_builder_releases_the_record_the_dispatch_just_claimed() {
for granted in [false, true] {
let (state, _dir, session) = hermetic();
insert_builder(&state, session, "rust-engineer");
insert_builder(&state, session, "local-ops");
let req = dispatch("python-engineer", Some("toolu_DENIED"));
record_the_preceding_claim_wrote(&state, session, &req.payload, granted);
let recorded = state.find_delegation(session, |d| {
d.tool_use_id.as_deref() == Some("toolu_DENIED")
});
assert!(
recorded.is_some(),
"premise (granted={granted}): the preceding claim records this dispatch"
);
let body =
builder_slot_op(&state, &session.0.to_string(), req, 2).expect("route succeeds");
assert!(!body.claimed, "granted={granted}: the machine is full");
let id = state
.find_delegation(session, |d| {
d.tool_use_id.as_deref() == Some("toolu_DENIED")
})
.expect("the record is retained, not removed");
let released = state
.all_delegations()
.into_iter()
.find(|d| d.id == id)
.expect("delegation");
assert!(
!released.status.is_live(),
"granted={granted}: a denied dispatch must not keep a live record: {:?}",
released.status
);
assert!(released.ended_at.is_some(), "granted={granted}");
assert!(
state
.shared_tree_occupants(std::path::Path::new("/repo"), Some("toolu_RETRY"))
.is_empty(),
"granted={granted}: the denied dispatch still occupies the checkout"
);
let (occupants, claimed) = state.claim_shared_tree_dispatch(
std::path::Path::new("/repo"),
Some("toolu_RETRY"),
true,
crate::daemon::state::sessions::SharedTreeQuestion::Dispatch,
|_| {},
);
assert!(
claimed,
"granted={granted}: the re-issued dispatch must be admitted, \
but #4480 named {occupants:?}"
);
}
}
#[test]
fn a_denied_builder_tombstones_a_record_that_lands_after_the_deny_8012() {
for isolation in [None, Some("worktree")] {
let (state, _dir, session) = hermetic();
insert_builder(&state, session, "rust-engineer");
insert_builder(&state, session, "local-ops");
let mut req = dispatch("python-engineer", Some("toolu_LATE"));
if let Some(mode) = isolation {
req.payload["input"]["isolation"] = Value::String(mode.to_string());
}
let payload = req.payload.clone();
assert!(
state
.find_delegation(session, |d| d.tool_use_id.as_deref() == Some("toolu_LATE"))
.is_none(),
"premise ({isolation:?}): nothing has recorded this dispatch yet"
);
let body =
builder_slot_op(&state, &session.0.to_string(), req, 2).expect("route succeeds");
assert!(!body.claimed, "{isolation:?}: the machine is full");
crate::daemon::services::delegation_tracker::observe(
&state,
session,
HookEvent::PreToolUse,
&payload,
);
let id = state
.find_delegation(session, |d| d.tool_use_id.as_deref() == Some("toolu_LATE"))
.expect("the tombstone or the late record carries the id");
let record = state
.all_delegations()
.into_iter()
.find(|d| d.id == id)
.expect("delegation");
assert!(
!record.status.is_live(),
"{isolation:?}: a denied dispatch must not end up live: {:?}",
record.status
);
assert_eq!(
state.builder_slot_holders(None).len(),
2,
"{isolation:?}: the denied dispatch must hold no builder slot"
);
assert!(
state
.shared_tree_occupants(std::path::Path::new("/repo"), Some("toolu_RETRY"))
.is_empty(),
"{isolation:?}: the denied dispatch still occupies the checkout"
);
}
}
#[test]
fn a_denied_builder_releases_only_its_own_record() {
let (state, _dir, session) = hermetic();
insert_builder(&state, session, "rust-engineer");
insert_builder(&state, session, "local-ops");
let req = dispatch("python-engineer", Some("toolu_DENIED"));
record_the_preceding_claim_wrote(&state, session, &req.payload, false);
let body = builder_slot_op(&state, &session.0.to_string(), req, 2).expect("route succeeds");
assert!(!body.claimed);
assert_eq!(
state.builder_slot_holders(None).len(),
2,
"the two holders keep their slots"
);
}
#[test]
fn an_admitted_builder_keeps_its_record() {
let (state, _dir, session) = hermetic();
let body = builder_slot_op(
&state,
&session.0.to_string(),
dispatch("rust-engineer", Some("toolu_OK")),
2,
)
.expect("route succeeds");
assert!(body.claimed);
assert_eq!(state.builder_slot_holders(None).len(), 1);
}
#[test]
fn a_payload_with_no_tool_use_id_is_ineligible_not_full() {
let (state, _dir, session) = hermetic();
let body = builder_slot_op(
&state,
&session.0.to_string(),
dispatch("rust-engineer", None),
4,
)
.expect("route succeeds");
assert!(!body.claimed);
assert!(
body.ineligible,
"an unclaimable payload must not read as a full machine"
);
assert!(state.builder_slot_holders(None).is_empty());
}
#[test]
fn a_full_machine_is_not_reported_ineligible() {
let (state, _dir, session) = hermetic();
insert_builder(&state, session, "rust-engineer");
let body = builder_slot_op(
&state,
&session.0.to_string(),
dispatch("python-engineer", Some("toolu_C")),
1,
)
.expect("route succeeds");
assert!(!body.claimed);
assert!(!body.ineligible);
}
#[tokio::test]
async fn builder_slot_census_route_reports_holders_and_the_cap() {
let (state, _dir, session) = hermetic();
insert_builder(&state, session, "rust-engineer");
let Json(census) = builder_slot_census_route(State(Arc::clone(&state))).await;
assert_eq!(census.holders.len(), 1);
assert_eq!(census.holders[0].agent, "rust-engineer");
assert!(census.expired.is_empty());
assert!(census.cap <= 64);
}
}