Fix rules files not loading and config file rescan clearing tokens (#53659)

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.
This commit is contained in:
Anders Jenbo 2026-04-21 10:30:31 +02:00 committed by GitHub
parent 95b0c5aeef
commit 4a630f0725
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 24 additions and 17 deletions

View file

@ -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)

View file

@ -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;
}
}