Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
test(agentic-core): repair baseline runtime contracts
  • Loading branch information
Fabricio Gonçalves da Silva Fabricio Gonçalves da Silva
Fabricio Gonçalves da Silva authored and Fabricio Gonçalves da Silva committed Aug 11, 2026
commit 594ece771c3314c2b47ea3e8ec54fe38d8c5a3cc
22 changes: 13 additions & 9 deletions src/apps/server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,10 @@ async fn main() -> Result<()> {
})
.collect::<Result<Vec<_>>>()?;

// This is a narrow controller/observer capability. It deliberately does
// not initialize the Server Host's dormant Agent Runtime: authoritative
// sessions and execution stay inside the target-side `bitfun dispatch`
// worker.
// This dispatch state is a narrow controller/observer capability. It does
// not create a second Agent Runtime: browser WebSocket sessions use the
// shared in-process runtime initialized above, while detached dispatch
// execution stays inside the target-side `bitfun dispatch` worker.
let path_manager = Arc::new(bitfun_core::infrastructure::PathManager::new()?);
let ssh_data_dir = dirs::data_local_dir()
.ok_or_else(|| anyhow::anyhow!("Could not resolve the local data directory"))?
Expand Down Expand Up @@ -267,7 +267,7 @@ mod tests {
}

#[test]
fn agent_bootstrap_reuses_core_ownership_without_activating_the_http_shell() {
fn agent_bootstrap_reuses_core_ownership_without_creating_a_parallel_runtime() {
let bootstrap = include_str!("bootstrap.rs");
assert!(bootstrap.contains("CoreRuntimeOwnership::embedded"));
let coordinator = bootstrap
Expand All @@ -292,12 +292,16 @@ mod tests {
.next()
.expect("Server production entrypoint");
assert!(
!main_source.contains("bootstrap::initialize"),
"the current read-only HTTP shell must not silently start an Agent Runtime"
main_source.contains("let server_state = bootstrap::initialize("),
"the Server Host must initialize the shared Agent Runtime used by WebSocket app-server sessions"
);
assert!(
main_source.contains("DispatchHostState"),
"the lightweight Server Host should expose dispatch without booting an Agent Runtime"
main_source.contains("let bitfun_app_server = app_server::build("),
"the Server Host must build the in-process app-server from the initialized runtime"
);
assert!(
main_source.contains(".layer(axum::Extension(bitfun_app_server))"),
"the Server Host must expose the initialized app-server to WebSocket routes"
);
}
}
27 changes: 24 additions & 3 deletions src/crates/services/terminal/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1946,6 +1946,7 @@ mod tests {
#[tokio::test]
async fn delayed_poll_returns_unread_output_after_process_exit() {
let manager = ExecProcessManager::default();
let (lifecycle_tx, mut lifecycle_rx) = tokio::sync::mpsc::unbounded_channel();
#[cfg(windows)]
let script = "echo first & powershell -NoProfile -Command \"Start-Sleep -Milliseconds 250\" & echo second";
#[cfg(not(windows))]
Expand All @@ -1959,7 +1960,7 @@ mod tests {
tty: false,
yield_time_ms: Some(100),
max_output_chars: Some(10_000),
lifecycle_tx: None,
lifecycle_tx: Some(lifecycle_tx),
output_capture_tx: None,
})
.await
Expand All @@ -1970,7 +1971,24 @@ mod tests {
.expect("process should still be running after first yield");
assert!(first.output.contains("first"));

tokio::time::sleep(std::time::Duration::from_millis(600)).await;
let running = tokio::time::timeout(std::time::Duration::from_secs(2), lifecycle_rx.recv())
.await
.expect("running lifecycle event should arrive")
.expect("lifecycle channel should stay open");
assert_eq!(running.session_id, session_id);
assert_eq!(running.status, ExecProcessLifecycleStatus::Running);

let exited = tokio::time::timeout(std::time::Duration::from_secs(5), lifecycle_rx.recv())
.await
.expect("exit lifecycle event should arrive")
.expect("lifecycle channel should stay open until exit");
assert_eq!(exited.session_id, session_id);
assert_eq!(exited.status, ExecProcessLifecycleStatus::Exited);
assert_eq!(
exited.exit_code,
Some(0),
"a naturally exited process must retain its real exit code"
);

let second = manager
.write_stdin(WriteStdinRequest {
Expand All @@ -1983,8 +2001,11 @@ mod tests {
.await
.expect("poll should return unread output");

assert!(
second.session_id.is_none(),
"the lifecycle exit event must not be published before the session is closed"
);
assert_eq!(second.exit_code, Some(0));
assert!(second.session_id.is_none());
assert!(second.output.contains("second"));
}

Expand Down