Speed up TUI startup by reusing plugin discovery (#26469)

## Summary

TUI startup loads related plugin data from `hooks/list`, session MCP
initialization, and plugin skill warmup. These paths repeated filesystem
discovery and emitted the same plugin warnings, while `hooks/list` and
account/model bootstrap ran serially.

This change:

- Reuses one immutable plugin load outcome across startup consumers.
- Keys the cache only on plugin-relevant configuration.
- Single-flights concurrent plugin loads and prevents invalidated loads
from repopulating the cache.
- Runs hook discovery and account/model bootstrap concurrently.
- Preserves configuration-migration ordering, hook review behavior, and
accurate startup telemetry.

In 10 alternating release-build launches in the Ruff repository with the
existing `~/.codex` configuration, median time to the first editable
composer decreased from 833ms to 504ms. The branch was faster in 9 of 10
pairs, with a paired median improvement of 312ms.
This commit is contained in:
Charlie Marsh authored and GitHub committed 2026-06-05 15:32:43 -04:00
1 parent 345cf6e8d0
commit 055c7a7c53
9 files changed
+303 -37

No files matched your search

+9 -4
View File
@@ -16,6 +16,7 @@ use crate::app_event::RealtimeAudioDeviceKind;
#[cfg(target_os = "windows")]
use crate::app_event::WindowsSandboxEnableMode;
use crate::app_event_sender::AppEventSender;
use crate::app_server_session::AppServerBootstrap;
use crate::app_server_session::AppServerSession;
use crate::app_server_session::AppServerStartedThread;
use crate::app_server_session::TurnPermissionsOverride;
@@ -727,6 +728,8 @@ impl App {
app_server_target: AppServerTarget,
state_db: Option<StateDbHandle>,
environment_manager: Arc<EnvironmentManager>,
startup_elapsed_before_app: Duration,
startup_bootstrap: Option<AppServerBootstrap>,
startup_hooks_browser: Option<HooksListEntry>,
) -> Result<AppExitInfo> {
use tokio_stream::StreamExt;
@@ -774,9 +777,11 @@ impl App {
});
}
};
let bootstrap_started_at = Instant::now();
let bootstrap = app_server.bootstrap(&config).await?;
let bootstrap_ms = bootstrap_started_at.elapsed().as_millis();
let bootstrap = match startup_bootstrap {
Some(bootstrap) => bootstrap,
None => app_server.bootstrap(&config).await?,
};
let bootstrap_ms = bootstrap.duration.as_millis();
let mut model = bootstrap.default_model;
let available_models = bootstrap.available_models;
let remote_connection = crate::status::remote_connection::remote_connection_status_value(
@@ -1085,7 +1090,7 @@ See the Codex keymap documentation for supported actions and examples."
tui.frame_requester().schedule_frame();
tracing::info!(
duration_ms = %startup_started_at.elapsed().as_millis(),
duration_ms = %(startup_elapsed_before_app + startup_started_at.elapsed()).as_millis(),
bootstrap_ms = %bootstrap_ms,
runtime_model_provider_ms = %runtime_model_provider_ms,
thread_and_widget_ms = %thread_and_widget_ms,