mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-09 01:49:49 -10:00
perf(tui): recheck user command folders at most once a second
Found by a read-only Gemini 4 Argon performance review (agy; CW/artifacts/improve-sweep-20261018/agy-gemini.md, finding 1), confirmed by reading user_registry.rs. While a `/` command is in the composer, render() reaches with_registry_for_workspace through visible_slash_menu_entries on every frame. That built command_dirs_snapshot_with_plugins before checking whether anything had changed: two is_workspace_trusted calls (each reads and parses ~/.codewhale/config.toml and canonicalizes every [projects] path), plus metadata and read_dir over up to eight command and workflow directories and every plugin command directory. During streaming that is dozens of times a second. The registry state now records when the snapshot was last compared. Within SNAPSHOT_RECHECK_INTERVAL (1 s) a read for the same workspace and extension epoch returns the cached registry without touching the disk. install_plugin_registry still forces a reload (initialized = false), and an extension-epoch change still reloads at once. Under cfg(test) the interval is zero, so tests that write a command file and read it back are unchanged. UserCommandRegistryState drops its unused Clone derive (the new Mutex field is not Clone). Live check (debug TUI, cw7087 harness): typed `/zzh` (no match), wrote ~/.codewhale/commands/zzhello.md, then after about 2.5 s the slash menu listed "/zzhello test command for the registry recheck" (slash-after-file.txt in the sweep folder). Checks: cargo test -p codewhale-tui --lib -- commands:: slash_menu user_registry user_commands: 1234 passed, 1 failed. The failure is diagnostics_adapter_projects_full_context_and_prepared_tool_json, which also failed under the full parallel run earlier today; alone it passed 3/3. cargo check clean; clippy (tui, CI flags) clean. Not addressed from the same finding: with_registry_for_plugins still clones the registry and verifies each plugin command's state file on every call. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NBKbhx19BRbZtvWK8rVKjz
This commit is contained in:
1 parent
9700a17cab
commit
d5f6699cc9
3 files changed
+41
-1
No files matched your search
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- While a slash command is in the composer, the TUI rereads your command
|
||||
folders and `config.toml` at most once a second instead of on every frame.
|
||||
A command file you add still shows up in completion within about a second.
|
||||
- In a session long enough to fold its oldest transcript cells (past
|
||||
5,000), tool details, expanded tool runs, sub-agent cards and a streaming
|
||||
reply keep pointing at the right rows. Each fold had shifted them one row
|
||||
|
||||
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- While a slash command is in the composer, the TUI rereads your command
|
||||
folders and `config.toml` at most once a second instead of on every frame.
|
||||
A command file you add still shows up in completion within about a second.
|
||||
- In a session long enough to fold its oldest transcript cells (past
|
||||
5,000), tool details, expanded tool runs, sub-agent cards and a streaming
|
||||
reply keep pointing at the right rows. Each fold had shifted them one row
|
||||
|
||||
@@ -17,7 +17,7 @@ use super::user_commands;
|
||||
|
||||
static USER_COMMAND_REGISTRY: OnceLock<RwLock<UserCommandRegistryState>> = OnceLock::new();
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Default)]
|
||||
struct UserCommandRegistryState {
|
||||
initialized: bool,
|
||||
workspace: Option<PathBuf>,
|
||||
@@ -29,6 +29,28 @@ struct UserCommandRegistryState {
|
||||
/// reloads when the live extension commands may have changed.
|
||||
extension_epoch: u64,
|
||||
registry: UserCommandRegistry,
|
||||
snapshot_checked_at: std::sync::Mutex<Option<std::time::Instant>>,
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
const SNAPSHOT_RECHECK_INTERVAL: std::time::Duration = std::time::Duration::from_secs(1);
|
||||
#[cfg(test)]
|
||||
const SNAPSHOT_RECHECK_INTERVAL: std::time::Duration = std::time::Duration::ZERO;
|
||||
|
||||
impl UserCommandRegistryState {
|
||||
fn checked_recently(&self) -> bool {
|
||||
self.snapshot_checked_at
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.is_some_and(|at| at.elapsed() < SNAPSHOT_RECHECK_INTERVAL)
|
||||
}
|
||||
|
||||
fn mark_checked(&self) {
|
||||
*self
|
||||
.snapshot_checked_at
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(std::time::Instant::now());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -790,6 +812,16 @@ pub fn with_registry_for_workspace<R>(
|
||||
) -> R {
|
||||
let workspace = normalize_workspace(workspace);
|
||||
let lock = registry_lock();
|
||||
{
|
||||
let guard = lock.read().expect("user command registry lock poisoned");
|
||||
if guard.initialized
|
||||
&& guard.workspace == workspace
|
||||
&& guard.extension_epoch == crate::extension_host::command::epoch()
|
||||
&& guard.checked_recently()
|
||||
{
|
||||
return f(&guard.registry);
|
||||
}
|
||||
}
|
||||
let (plugin_sources, plugin_errors) = {
|
||||
let guard = lock.read().expect("user command registry lock poisoned");
|
||||
if guard.plugin_workspace == workspace {
|
||||
@@ -806,6 +838,7 @@ pub fn with_registry_for_workspace<R>(
|
||||
{
|
||||
let guard = lock.read().expect("user command registry lock poisoned");
|
||||
if !registry_needs_reload(&guard, &workspace, &snapshot, extension_epoch) {
|
||||
guard.mark_checked();
|
||||
return f(&guard.registry);
|
||||
}
|
||||
}
|
||||
@@ -827,6 +860,7 @@ pub fn with_registry_for_workspace<R>(
|
||||
guard.extension_epoch = extension_epoch;
|
||||
guard.registry = replacement;
|
||||
}
|
||||
guard.mark_checked();
|
||||
f(&guard.registry)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user