fix(state): jangan bangun mention index di mode attach

AppStateRest::new() men-spawn thread ignore::Walk untuk mention_index
tanpa syarat, padahal mode --attach cuma dipakai untuk render lokal —
handle_key dan logika mention berjalan di sisi daemon lewat IPC, jadi
index di client attach tidak pernah dipakai. Ini bikin setiap
--attach melakukan full workspace walk (sampai 50.000 entry) sia-sia.

Pindahkan thread-spawn itu ke method terpisah
spawn_mention_index_build(), dipanggil eksplisit dari
run_single_process() dan run_daemon() setelah AppStateRest::new(),
tapi sengaja tidak dipanggil dari run_attach().
This commit is contained in:
asepharyana
2026-07-15 06:32:57 +07:00
parent dcfc5b9ec0
commit e0d0860d7b
2 changed files with 48 additions and 37 deletions
+29 -20
View File
@@ -206,23 +206,35 @@ impl AppStateRest {
}); });
} }
// Fire-and-forget background file index build for `@file` mention state
// autocomplete. }
//
// Flow: spawn OS thread -> `ignore::Walk` each workspace root, /// Spawn the background thread that walks every workspace root and
// collecting file paths (workspace-index-prefixed for roots beyond /// populates `mention_index` for `@file` mention autocomplete.
// the first, matching `resolve_path`'s `[N]path` convention) -> stop ///
// once 50,000 entries are collected -> store the result in /// Why a separate method, not called from `new()`: the attach-only
// `mention_index`. /// TUI client also constructs an `AppStateRest` (for local rendering
// /// state) but never runs tools or `handle_key` locally — it forwards
// Why a raw thread and not a background tokio task: there is no /// keystrokes to the daemon over IPC, which has its own `AppStateRest`
// persistent async runtime driving the render loop, and this is /// with its own index. Spawning this walk in the attach client would
// blocking filesystem I/O -- a dedicated thread keeps startup /// waste a full workspace scan for an index nothing there consumes.
// non-blocking. Not joined, same rationale as the LSP provisioning /// Callers that DO need the index (single-process mode, the daemon)
// thread above: a slow/huge repo must not delay the TUI appearing. /// call this explicitly after construction.
{ ///
let mention_index = state.mention_index.clone(); /// Flow: spawn OS thread -> `ignore::Walk` each workspace root,
let roots = state.workspace_roots.clone(); /// collecting file paths (workspace-index-prefixed for roots beyond
/// the first, matching `resolve_path`'s `[N]path` convention) -> stop
/// once 50,000 entries are collected -> store the result in
/// `mention_index`.
///
/// Why a raw thread and not a background tokio task: there is no
/// persistent async runtime driving the render loop, and this is
/// blocking filesystem I/O -- a dedicated thread keeps startup
/// non-blocking. Not joined, same rationale as the LSP provisioning
/// thread above: a slow/huge repo must not delay the TUI appearing.
pub fn spawn_mention_index_build(&self) {
let mention_index = self.mention_index.clone();
let roots = self.workspace_roots.clone();
std::thread::spawn(move || { std::thread::spawn(move || {
const MAX_MENTION_ENTRIES: usize = 50_000; const MAX_MENTION_ENTRIES: usize = 50_000;
let mut paths = Vec::new(); let mut paths = Vec::new();
@@ -244,9 +256,6 @@ impl AppStateRest {
}); });
} }
state
}
/// Whether an agent turn is currently running. /// Whether an agent turn is currently running.
/// ///
/// Return: `false` (and logs a warning) if the mutex is poisoned, rather /// Return: `false` (and logs a warning) if the mutex is poisoned, rather
+2
View File
@@ -109,6 +109,7 @@ fn run_single_process() -> Result<()> {
&session_dir, &session_dir,
store.memory_dir, store.memory_dir,
); );
state.spawn_mention_index_build();
state.sessions = model::session::Session::list(&store.base_dir); state.sessions = model::session::Session::list(&store.base_dir);
@@ -442,6 +443,7 @@ fn run_daemon() -> Result<()> {
&session_dir, &session_dir,
store.memory_dir, store.memory_dir,
); );
state.spawn_mention_index_build();
state.sessions = model::session::Session::list(&store.base_dir); state.sessions = model::session::Session::list(&store.base_dir);
let _rt = tokio::runtime::Runtime::new()?; let _rt = tokio::runtime::Runtime::new()?;