From 4a630f0725658c602f278c91138603ae10aaa0b6 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Tue, 21 Apr 2026 10:30:31 +0200 Subject: [PATCH] Fix rules files not loading and config file rescan clearing tokens (#53659) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #52453 Fixes #53246 Both issues were introduced by #51208 ("Handle Linux FS Rescan Events"), which added `PathEventKind::Rescan` handling. ## Rules files not loading (#52453) `load_worktree_info_for_system_prompt` called `load_worktree_rules_file` synchronously, which uses `entry_for_path()` on the current worktree snapshot. If the background scanner hasn't finished its initial scan, the entry doesn't exist yet and the lookup returns `None` — the code concludes no rules file exists. This was always a latent race condition, but became more visible after Rescan events were introduced, since they can trigger additional `WorktreeUpdatedEntries` churn that interacts with the refresh mechanism. The fix awaits `scan_complete()` on local worktrees before performing the rules file lookup, ensuring the full directory tree is indexed first. ## Config file rescan clearing OAuth tokens (#53246) The `Rescan` handlers in `watch_config_dir` used `fs.load(file_path).await.unwrap_or_default()`, which turns any file-read error into an empty string. This empty string flows to consumers like `CopilotChat`, where `extract_oauth_token("")` returns `None`, causing the OAuth token to be unconditionally overwritten with `None` — triggering re-authentication. The fix changes both Rescan handlers to skip files that fail to load (using `if let Ok(contents) = ...`), matching the pattern already used by the `Created`/`Changed` handler. Release Notes: - Fixed rules files (AGENTS.md, CLAUDE.md, .rules, etc.) sometimes not being applied in agent threads. - Fixed GitHub Copilot re-prompting for authentication after filesystem rescan events. --- crates/agent/src/agent.rs | 31 ++++++++++++++++------------ crates/settings/src/settings_file.rs | 10 +++++---- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/crates/agent/src/agent.rs b/crates/agent/src/agent.rs index 160172c5c49d3af5548ccd76af1e441662fadfbb..d948e2f7bc7f2edb02a2ad376f6f56bdc5ec9d91 100644 --- a/crates/agent/src/agent.rs +++ b/crates/agent/src/agent.rs @@ -591,6 +591,7 @@ impl NativeAgent { let tree = worktree.read(cx); let root_name = tree.root_name_str().into(); let abs_path = tree.abs_path(); + let scan_complete = tree.as_local().map(|local| local.scan_complete()); let mut context = WorktreeContext { root_name, @@ -598,20 +599,24 @@ impl NativeAgent { rules_file: None, }; - let rules_task = Self::load_worktree_rules_file(worktree, project, cx); - let Some(rules_task) = rules_task else { - return Task::ready((context, None)); - }; + cx.spawn(async move |cx| { + if let Some(scan_complete) = scan_complete { + scan_complete.await; + } - cx.spawn(async move |_| { - let (rules_file, rules_file_error) = match rules_task.await { - Ok(rules_file) => (Some(rules_file), None), - Err(err) => ( - None, - Some(RulesLoadingError { - message: format!("{err}").into(), - }), - ), + let rules_task = cx.update(|cx| Self::load_worktree_rules_file(worktree, project, cx)); + + let (rules_file, rules_file_error) = match rules_task { + Some(rules_task) => match rules_task.await { + Ok(rules_file) => (Some(rules_file), None), + Err(err) => ( + None, + Some(RulesLoadingError { + message: format!("{err}").into(), + }), + ), + }, + None => (None, None), }; context.rules_file = rules_file; (context, rules_file_error) diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 258deaa9ee4c64119601fabfdae38b79c654c81b..8b4187da0093c7759d7ba20ee95644293788a57a 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -232,8 +232,9 @@ pub fn watch_config_dir( } Some(PathEventKind::Rescan) => { for file_path in &config_paths { - let contents = fs.load(file_path).await.unwrap_or_default(); - if tx.unbounded_send(contents).is_err() { + if let Ok(contents) = fs.load(file_path).await + && tx.unbounded_send(contents).is_err() + { return; } } @@ -244,8 +245,9 @@ pub fn watch_config_dir( && event.path == dir_path { for file_path in &config_paths { - let contents = fs.load(file_path).await.unwrap_or_default(); - if tx.unbounded_send(contents).is_err() { + if let Ok(contents) = fs.load(file_path).await + && tx.unbounded_send(contents).is_err() + { return; } }