Description
#3829 fixed load_instructions blocking I/O in runner.rs (startup) and agent/mod.rs (hot-reload watcher) by wrapping them in spawn_blocking. However, a third async call site was not addressed.
provider_cmd.rs::update_provider_instructions calls load_instructions synchronously. It is invoked via provider_switch_as_string → handle_provider_command_as_string, which is wrapped in:
// agent_access_impl.rs:518
Box::pin(async move { self.handle_provider_command_as_string(arg) })
This async block has no .await points, but the synchronous filesystem I/O (canonicalize, File::open, read_to_string, read_dir) still executes on the Tokio worker thread polling the future.
The PR #3854 commit message described this as "the remaining non-async call site" — but the call site is inside an async move block, which is an async context.
Reproduction Steps
- Configure multiple providers with instruction files.
- Run
cargo run --features full -- --config .local/config/testing.toml.
- Type
/provider <other-provider-name> to trigger a provider switch.
- Blocking I/O runs on Tokio worker thread during provider switch.
Expected Behavior
update_provider_instructions should use load_instructions_async (or mark itself async and await it), as the other two call sites now do.
Actual Behavior
Synchronous filesystem I/O runs on Tokio worker thread during /provider command.
Environment
- Version: HEAD (c16c4dc)
- File:
crates/zeph-core/src/agent/provider_cmd.rs:317, crates/zeph-core/src/agent/agent_access_impl.rs:518
- Severity: Low (infrequent path — only on explicit
/provider command)
Description
#3829fixedload_instructionsblocking I/O inrunner.rs(startup) andagent/mod.rs(hot-reload watcher) by wrapping them inspawn_blocking. However, a third async call site was not addressed.provider_cmd.rs::update_provider_instructionscallsload_instructionssynchronously. It is invoked viaprovider_switch_as_string → handle_provider_command_as_string, which is wrapped in:This async block has no
.awaitpoints, but the synchronous filesystem I/O (canonicalize,File::open,read_to_string,read_dir) still executes on the Tokio worker thread polling the future.The PR #3854 commit message described this as "the remaining non-async call site" — but the call site is inside an
async moveblock, which is an async context.Reproduction Steps
cargo run --features full -- --config .local/config/testing.toml./provider <other-provider-name>to trigger a provider switch.Expected Behavior
update_provider_instructionsshould useload_instructions_async(or mark itselfasyncand await it), as the other two call sites now do.Actual Behavior
Synchronous filesystem I/O runs on Tokio worker thread during
/providercommand.Environment
crates/zeph-core/src/agent/provider_cmd.rs:317,crates/zeph-core/src/agent/agent_access_impl.rs:518/providercommand)