Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
dd212eb
fix(core): give the JSON-RPC runtime a 16 MiB worker stack to survive…
sanil-23 Jun 1, 2026
d5d5a64
test: green the Rust Core Coverage suite (stale assertions + env-race…
sanil-23 Jun 1, 2026
b389418
Merge branch 'fix/subagent-stack-overflow' into fix/coverage-stale-tests
sanil-23 Jun 1, 2026
4f6f891
Merge remote-tracking branch 'upstream/main' into fix/coverage-stale-…
sanil-23 Jun 1, 2026
543b847
test(coverage): serialize env-racing raw-coverage tests with env_lock…
sanil-23 Jun 1, 2026
99276a3
Revert "test(coverage): serialize env-racing raw-coverage tests with …
sanil-23 Jun 1, 2026
c2af5bc
test: update stale composio-enabled count assertion (#3113 sync redes…
sanil-23 Jun 1, 2026
695bacc
test: make round21 github-reader hermetic + drop stale comments asser…
sanil-23 Jun 1, 2026
a1b5484
test: make github-reader tests hermetic in memory_sync_sources + near90
sanil-23 Jun 1, 2026
be29029
test: responses-API input content is structured parts, not a bare string
sanil-23 Jun 1, 2026
89c4325
test: fix time-bomb in cron-add coverage test (hardcoded past 'at' date)
sanil-23 Jun 1, 2026
13d9cbf
test: give ApprovalGate test session_ids the required `session-` prefix
sanil-23 Jun 1, 2026
494579f
Merge remote-tracking branch 'upstream/main' into fix/coverage-stale-…
sanil-23 Jun 1, 2026
0e46854
ci(coverage): serialize core llvm-cov tests (--test-threads=1)
sanil-23 Jun 1, 2026
657781a
test(coverage): hold env_lock in orchestrator_tool_synthesis (fast ra…
sanil-23 Jun 1, 2026
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
9 changes: 9 additions & 0 deletions src/core/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,17 @@ fn run_server_command(args: &[String]) -> Result<()> {
crate::core::logging::init_for_cli_run(verbose, log_scope);

// Initialize the Tokio multi-threaded runtime.
//
// A single agent turn is a very large async state machine (system prompt +
// hundreds of tool specs + the nested provider/tool loop), and delegating
// to a sub-agent runs another full turn one level down. Even with the inner
// sub-agent future boxed (`subagent_runner::ops`), that nesting overflows
// tokio's default 2 MiB worker-thread stack and aborts the whole process
// (SIGABRT: "thread 'tokio-rt-worker' has overflowed its stack"), taking
// the JSON-RPC server down mid-request. Give workers a roomier stack.
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.thread_stack_size(16 * 1024 * 1024)
.build()?;
rt.block_on(async {
crate::core::jsonrpc::run_server(host.as_deref(), port, socketio_enabled).await
Expand Down
2 changes: 1 addition & 1 deletion tests/memory_raw_coverage_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ fn memory_sources_validation_and_sync_classification_edges() {
assert_eq!(classify_unknown("GMAIL_FETCH_EMAILS"), ToolScope::Read);
assert_eq!(
toolkit_from_slug(" MICROSOFT_TEAMS_SEND "),
Some("microsoft".into())
Some("microsoft_teams".into())
);
assert_eq!(toolkit_from_slug(""), None);
let catalog = [CuratedTool {
Expand Down
24 changes: 21 additions & 3 deletions tests/memory_threads_raw_coverage_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,7 @@ async fn memory_source_status_counts_reader_and_composio_prefixes() {
#[tokio::test]
async fn memory_thread_tree_and_sync_controller_schemas_execute_public_handlers() {
let tmp = TempDir::new().expect("tempdir");
let _env_lock = env_lock();
let _workspace = EnvVarGuard::set_to_path("OPENHUMAN_WORKSPACE", tmp.path());
let config = Config::load_or_init().await.expect("init isolated config");

Expand Down Expand Up @@ -989,7 +990,7 @@ fn memory_schema_registries_and_query_tool_metadata_cover_public_surfaces() {
let legacy_tree_schemas = openhuman_core::openhuman::memory::schema::all_controller_schemas();
let legacy_tree_controllers =
openhuman_core::openhuman::memory::schema::all_registered_controllers();
assert_eq!(legacy_tree_schemas.len(), 19);
assert_eq!(legacy_tree_schemas.len(), 21);
assert_eq!(legacy_tree_schemas.len(), legacy_tree_controllers.len());
for function in [
"ingest",
Expand Down Expand Up @@ -1208,7 +1209,7 @@ fn memory_sync_composio_catalog_scope_and_state_helpers_cover_edge_cases() {
assert_eq!(classify_unknown("GMAIL_FETCH_EMAILS"), ToolScope::Read);
assert_eq!(
toolkit_from_slug(" MICROSOFT_TEAMS_SEND_MESSAGE "),
Some("microsoft".into())
Some("microsoft_teams".into())
);
assert_eq!(toolkit_from_slug(""), None);
let catalog = &[CuratedTool {
Expand Down Expand Up @@ -3479,6 +3480,7 @@ fn turn_state_store_persists_lists_marks_and_clears_snapshots() {
#[tokio::test]
async fn threads_rpc_ops_cover_crud_title_fallback_and_turn_state_cleanup() {
let tmp = TempDir::new().expect("tempdir");
let _env_lock = env_lock();
let _workspace = EnvVarGuard::set_to_path("OPENHUMAN_WORKSPACE", tmp.path());
let config = Config::load_or_init().await.expect("init isolated config");
let workspace_dir = config.workspace_dir.clone();
Expand Down Expand Up @@ -3685,6 +3687,7 @@ async fn threads_rpc_ops_cover_crud_title_fallback_and_turn_state_cleanup() {
#[tokio::test]
async fn threads_title_generation_branches_cover_noop_and_not_found_paths() {
let tmp = TempDir::new().expect("tempdir");
let _env_lock = env_lock();
let _workspace = EnvVarGuard::set_to_path("OPENHUMAN_WORKSPACE", tmp.path());
Config::load_or_init().await.expect("init isolated config");

Expand Down Expand Up @@ -3755,13 +3758,14 @@ async fn threads_title_generation_branches_cover_noop_and_not_found_paths() {
#[tokio::test]
async fn memory_sources_registry_rpc_and_schema_handlers_cover_crud_edges() {
let tmp = TempDir::new().expect("tempdir");
let _env_lock = env_lock();
let _workspace = EnvVarGuard::set_to_path("OPENHUMAN_WORKSPACE", tmp.path());
Config::load_or_init().await.expect("init isolated config");
std::fs::write(tmp.path().join("reader-note.md"), "# Reader note").expect("write note");

let schemas = all_memory_sources_controller_schemas();
let controllers = all_memory_sources_registered_controllers();
assert_eq!(schemas.len(), 9);
assert_eq!(schemas.len(), 10);
assert_eq!(schemas.len(), controllers.len());
assert_eq!(
openhuman_core::openhuman::memory_sources::schemas::schemas("read_item").function,
Expand Down Expand Up @@ -3961,6 +3965,7 @@ async fn memory_sources_registry_rpc_and_schema_handlers_cover_crud_edges() {
#[tokio::test]
async fn memory_ops_public_handlers_cover_document_file_kv_graph_and_envelopes() {
let tmp = TempDir::new().expect("tempdir");
let _env_lock = env_lock();
let _workspace = EnvVarGuard::set_to_path("OPENHUMAN_WORKSPACE", tmp.path());

let init = openhuman_core::openhuman::memory::ops::memory_init(MemoryInitRequest {
Expand Down Expand Up @@ -4334,6 +4339,7 @@ async fn memory_ops_public_handlers_cover_document_file_kv_graph_and_envelopes()
#[tokio::test]
async fn memory_tree_retrieval_rpc_and_schema_wrappers_cover_empty_and_invalid_paths() {
let tmp = TempDir::new().expect("tempdir");
let _env_lock = env_lock();
let _workspace = EnvVarGuard::set_to_path("OPENHUMAN_WORKSPACE", tmp.path());
let config = config_in(&tmp);

Expand Down Expand Up @@ -4451,6 +4457,7 @@ async fn memory_tree_retrieval_rpc_and_schema_wrappers_cover_empty_and_invalid_p
#[tokio::test]
async fn memory_query_backend_and_tree_flush_wrappers_cover_public_edges() {
let tmp = TempDir::new().expect("tempdir");
let _env_lock = env_lock();
let _workspace = EnvVarGuard::set_to_path("OPENHUMAN_WORKSPACE", tmp.path());
let mut config = Config::load_or_init().await.expect("init isolated config");
config.memory_tree.embedding_endpoint = None;
Expand Down Expand Up @@ -4590,6 +4597,7 @@ async fn tree_summarizer_ops_cover_validation_query_and_local_provider_guards()
#[tokio::test]
async fn memory_sources_types_registry_and_sync_state_cover_public_persistence_edges() {
let tmp = TempDir::new().expect("tempdir");
let _env_lock = env_lock();
let _workspace = EnvVarGuard::set_to_path("OPENHUMAN_WORKSPACE", tmp.path());
let _config = Config::load_or_init().await.expect("init isolated config");
openhuman_core::openhuman::memory_sources::reconcile::ensure_composio_sources().await;
Expand Down Expand Up @@ -4800,3 +4808,13 @@ fn welcome_migration_public_entrypoint_covers_empty_marker_and_transcript_paths(
.expect("second migration");
assert!(second.already_done);
}

// Serializes this binary's process-global OPENHUMAN_WORKSPACE mutation so the
// raw-coverage tests do not trample each other under parallel llvm-cov runs.
static ENV_LOCK: std::sync::OnceLock<std::sync::Mutex<()>> = std::sync::OnceLock::new();
fn env_lock() -> std::sync::MutexGuard<'static, ()> {
ENV_LOCK
.get_or_init(|| std::sync::Mutex::new(()))
.lock()
.unwrap_or_else(|e| e.into_inner())
}
13 changes: 12 additions & 1 deletion tests/memory_tree_sync_raw_coverage_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ async fn memory_walk_provider_errors_and_unknown_actions_are_reported() {

#[tokio::test]
async fn composio_providers_sync_state_and_bus_surfaces_cover_read_write_edges() {
let _lock = env_lock();
let tmp = TempDir::new().expect("tempdir");
let _workspace = EnvVarGuard::set("OPENHUMAN_WORKSPACE", tmp.path());
let _triage = EnvVarGuard::set_str("OPENHUMAN_TRIGGER_TRIAGE_DISABLED", "yes");
Expand All @@ -458,7 +459,7 @@ async fn composio_providers_sync_state_and_bus_surfaces_cover_read_write_edges()
);
assert_eq!(
toolkit_from_slug("MICROSOFT_TEAMS_SEND_MESSAGE").as_deref(),
Some("microsoft")
Some("microsoft_teams")
);
assert_eq!(classify_unknown("GMAIL_DELETE_DRAFT"), ToolScope::Admin);
assert_eq!(classify_unknown("NOTION_CREATE_PAGE"), ToolScope::Write);
Expand Down Expand Up @@ -646,3 +647,13 @@ async fn default_composio_provider_hooks_return_expected_noop_shapes() {
.any(|entity| entity.canonical_id == "email:round14@example.com"));
assert!(approx_token_count("one two three four") > 0);
}

// Serializes this binary's process-global OPENHUMAN_WORKSPACE mutation so the
// raw-coverage tests do not trample each other under parallel llvm-cov runs.
static ENV_LOCK: std::sync::OnceLock<std::sync::Mutex<()>> = std::sync::OnceLock::new();
fn env_lock() -> std::sync::MutexGuard<'static, ()> {
ENV_LOCK
.get_or_init(|| std::sync::Mutex::new(()))
.lock()
.unwrap_or_else(|e| e.into_inner())
}
1 change: 1 addition & 0 deletions tests/near90_closure_raw_coverage_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ fn round20_credentials_profiles_cover_legacy_plaintext_errors_and_active_edges()

#[tokio::test]
async fn round20_memory_sources_readers_and_sync_cover_error_edges_without_network() {
let _lock = env_lock();
let _lock = env_lock();
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
let harness = setup("http://127.0.0.1:9");
let config = harness.config().await;
Expand Down
11 changes: 11 additions & 0 deletions tests/owned_domain_raw_coverage_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ fn owned_domain_config(workspace_root: &std::path::Path) -> Config {

#[tokio::test]
async fn openai_compatible_provider_covers_auth_temperature_tool_fallback_and_responses() {
let _lock = env_lock();
let (base_url, state) = serve_provider_mock().await;
let provider = OpenAiCompatibleProvider::new_with_user_agent(
"owned-mock",
Expand Down Expand Up @@ -873,3 +874,13 @@ async fn tool_registry_controller_handlers_cover_list_get_and_validation_paths()
.and_then(Value::as_u64)
.is_some_and(|count| count > 0));
}

// Serializes this binary's process-global OPENHUMAN_WORKSPACE mutation so the
// raw-coverage tests do not trample each other under parallel llvm-cov runs.
static ENV_LOCK: std::sync::OnceLock<std::sync::Mutex<()>> = std::sync::OnceLock::new();
fn env_lock() -> std::sync::MutexGuard<'static, ()> {
ENV_LOCK
.get_or_init(|| std::sync::Mutex::new(()))
.lock()
.unwrap_or_else(|e| e.into_inner())
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
1 change: 1 addition & 0 deletions tests/tool_registry_approval_raw_coverage_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,7 @@ async fn approval_schema_handlers_validate_params_and_surface_empty_gate_state()

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn approval_rpc_decision_paths_persist_always_allow_and_recent_audit() {
let _lock = env_lock();
let _lock = env_lock();
let harness = setup("").await;
let config = Config::load_or_init()
Expand Down
4 changes: 3 additions & 1 deletion tests/tools_agent_credentials_state_raw_coverage_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,9 @@ async fn round16_spawn_subagent_tool_and_runner_error_success_paths() {
.await
.expect("dedicated thread returns tool result");
assert!(disabled_thread.is_error);
assert!(disabled_thread.output().contains("temporarily disabled"));
// The dedicated_thread flag no longer short-circuits with a "temporarily
// disabled" message (see spawn_subagent::dedicated_thread_flag_no_longer_returns_disabled_error).
assert!(!disabled_thread.output().contains("temporarily disabled"));

let provider = Arc::new(ScriptedProvider::new(vec![response(
Some("subagent final answer that will be clipped"),
Expand Down
5 changes: 4 additions & 1 deletion tests/tools_composio_network_leftovers_raw_coverage_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ async fn round20_polymarket_covers_discovery_errors_rpc_allowance_and_write_gate

#[tokio::test]
async fn round20_spawn_subagent_covers_validation_schema_and_disabled_worker_branch() {
let _lock = env_lock();
let _lock = env_lock();
let tool = SpawnSubagentTool::new();

Expand Down Expand Up @@ -592,7 +593,9 @@ async fn round20_spawn_subagent_covers_validation_schema_and_disabled_worker_bra
.await
.expect("dedicated thread disabled returns tool result");
assert!(dedicated_thread.is_error);
assert!(dedicated_thread.output().contains("temporarily disabled"));
// The dedicated_thread flag no longer short-circuits with a "temporarily
// disabled" message (see spawn_subagent::dedicated_thread_flag_no_longer_returns_disabled_error).
assert!(!dedicated_thread.output().contains("temporarily disabled"));
}

async fn start_loopback(app: Router) -> String {
Expand Down
1 change: 1 addition & 0 deletions tests/worker_b_raw_coverage_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,7 @@ async fn agent_profile_lifecycle_persists_custom_profile_and_validates_delete()

#[tokio::test]
async fn approval_gate_rpc_decision_resumes_parked_tool_and_records_execution() {
let _lock = env_lock();
let _lock = env_lock();
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
let harness = setup().await;
let config = Config::load_or_init()
Expand Down
Loading