From 3c36f007365f184b860520dfb9b1bd97e01bf5cf Mon Sep 17 00:00:00 2001 From: ualtinok <94532+ualtinok@users.noreply.github.com> Date: Tue, 27 Oct 2026 13:35:16 +0100 Subject: [PATCH 1/2] Wait up to 30 s for the config move's second watched-file event lsp_diagnostics_test::move_file_reports_deleted_source_and_created_destination_for_config_file waited 2 s for the second of two asynchronous didChangeWatchedFiles notifications and failed on a loaded Linux runner with one received. The assertions on both events are unchanged. Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com> --- .../slice-descriptors/land-flake-batch-3.json | 12 ++++++++++++ crates/aft/tests/integration/lsp_diagnostics_test.rs | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 benchmarks/aft-search/slice-descriptors/land-flake-batch-3.json diff --git a/benchmarks/aft-search/slice-descriptors/land-flake-batch-3.json b/benchmarks/aft-search/slice-descriptors/land-flake-batch-3.json new file mode 100644 index 000000000..564a47658 --- /dev/null +++ b/benchmarks/aft-search/slice-descriptors/land-flake-batch-3.json @@ -0,0 +1,12 @@ +{ + "slice_class": "non_ranking", + "targeted_mechanism": "none", + "kind": "harness", + "fixtures": [ + "harness-goldens" + ], + "slices": [ + "flake-batch-3" + ], + "note": "Test-only: timing and race fixes for merge-queue flakes (generous positive waits, waits on real signals). No product search behavior changed and no changed path matches RANKING_FENCE_PREFIXES." +} diff --git a/crates/aft/tests/integration/lsp_diagnostics_test.rs b/crates/aft/tests/integration/lsp_diagnostics_test.rs index 8ed3001a5..e0c71fe5e 100644 --- a/crates/aft/tests/integration/lsp_diagnostics_test.rs +++ b/crates/aft/tests/integration/lsp_diagnostics_test.rs @@ -909,8 +909,10 @@ fn move_file_reports_deleted_source_and_created_destination_for_config_file() { let json = serde_json::to_value(&response).expect("response serializes"); assert_eq!(json["success"], true, "move failed: {json}"); + // Both notifications arrive asynchronously from the move; a loaded runner + // can deliver the second one well after 2 s, so wait generously for it. let mut events = drain_watched_file_events_from_ctx(&ctx); - let deadline = Instant::now() + Duration::from_secs(2); + let deadline = Instant::now() + Duration::from_secs(30); while events.len() < 2 && Instant::now() < deadline { thread::sleep(Duration::from_millis(25)); events.extend(drain_watched_file_events_from_ctx(&ctx)); From 9920be5579cda0b967d98146af921d296f0ef3cb Mon Sep 17 00:00:00 2001 From: ualtinok <94532+ualtinok@users.noreply.github.com> Date: Tue, 27 Oct 2026 13:56:53 +0100 Subject: [PATCH 2/2] mason: hold back the request's own trigram reload in the footer contract test issue_826_trigram_footer_agrees_with_a_scan_answer expects the footer `trigram: loading (this answer read files directly, no index)`. Its search request starts reloading the saved trigram cache (SearchLaneStatus::recover_evicted_trigram -> trigger_search_index_reload_if_evicted) and waits at most 250 ms for it. On Linux and on an idle Windows runner the reload finishes and is installed inside the request (the index is resident and ready afterwards), so the footer relabels a ready-but-unused lane as `loading`. On a loaded CI runner the reload can outlast the wait; the receiver is still installed when the footer is written and it truthfully says `trigram: building`. That is a test setup race, not a product bug: a reload really is in flight. Add a hidden AppContext test hook that engages the existing query-reload cooldown, call it in the test, and assert the request left no index or receiver behind. The footer then always comes from the not-resident, valid-header observation, and the exact footer assertion is kept. Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com> --- crates/aft/src/context.rs | 14 ++++++++++++++ .../integration/aft_search_contract_test.rs | 19 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/crates/aft/src/context.rs b/crates/aft/src/context.rs index e5098e8ad..ce47b29e7 100644 --- a/crates/aft/src/context.rs +++ b/crates/aft/src/context.rs @@ -12076,6 +12076,20 @@ impl AppContext { true } + /// Test hook: hold back query-triggered trigram reloads for the current + /// configure generation, as the retry cooldown in + /// `search_index_query_reload_allowed` does. A test can then observe a + /// root whose saved trigram cache is valid but not resident, without its + /// own search request starting a reload whose completion races the reply. + #[doc(hidden)] + pub fn defer_query_search_index_reloads_for_test(&self) { + const HOLD: Duration = Duration::from_secs(60 * 60); + let generation = self.configure_generation(); + let mut state = self.search_index_disconnect_reschedule.lock(); + let replacements = if state.0 == generation { state.1 } else { 0 }; + *state = (generation, replacements, Some(Instant::now() + HOLD)); + } + pub(crate) fn note_search_index_load_succeeded(&self) { let generation = self.configure_generation(); let mut state = self.search_index_disconnect_reschedule.lock(); diff --git a/crates/aft/tests/integration/aft_search_contract_test.rs b/crates/aft/tests/integration/aft_search_contract_test.rs index fe58e37d5..144752c36 100644 --- a/crates/aft/tests/integration/aft_search_contract_test.rs +++ b/crates/aft/tests/integration/aft_search_contract_test.rs @@ -3462,6 +3462,12 @@ fn issue_826_lane_footer_agrees_with_a_degraded_answer() { /// The trigram lane is not resident but its saved cache is valid. Planning /// treats that lane as still loading and answers from a filesystem scan; the /// footer used to call it ready beside `Search status: fully degraded`. +/// +/// The search request would otherwise start reloading the saved cache itself. +/// That reload usually finishes inside the request, but on a loaded machine it +/// can still be running when the footer is written, which then truthfully +/// says `trigram: building`. The test holds the reload back so the footer +/// always describes a valid cache that is not resident. #[test] fn issue_826_trigram_footer_agrees_with_a_scan_answer() { let _git_env = crate::test_helpers::hermetic_git_env_guard(); @@ -3473,12 +3479,25 @@ fn issue_826_trigram_footer_agrees_with_a_scan_answer() { let canonical = std::fs::canonicalize(project.path()).expect("canonical root"); ctx.set_canonical_cache_root(canonical.clone()); ctx.memoized_artifact_cache_key(&canonical); + ctx.defer_query_search_index_reloads_for_test(); install_ready_semantic_lane(&ctx, project.path()); let (rendered, response) = render_search(handle_semantic_search(&request("needle_symbol"), &ctx)); handle.join().expect("embedding server thread"); + assert!( + ctx.search_index_rx() + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_none() + && ctx + .search_index() + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .is_none(), + "the request must not have started loading the saved trigram cache" + ); assert_eq!(response["success"], true, "{response}"); assert_eq!(response["fully_degraded"], true, "{response}"); assert!(