Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1520,41 +1520,48 @@ impl CodeIndexWorktreeSchedulerV1 {
};
// Equal metadata is not currency. A quiet Noop is only honest when
// this pass decoded the generation and re-derived every sealed digest.
// Otherwise return None so the caller runs the content proof now;
// a later wake is not a substitute, because this empty-slot seat is
// the first branch and would keep swallowing the pass.
// A dirty remount (hints, config drift, or a moved frontier) must still
// seat from the durable pointer without joining the publication decode
// barrier — activation may already own that flight.
let frontier_sweep = self.retained_frontier_stat_sweep(&pointer);
let content_matches = decoded.as_ref().is_some_and(|generation| {
self.retained_frontier_stat_sweep(&pointer)
.is_some_and(|sweep| {
sweep.content_matches(
&self.project_root,
&SourceContentManifestV1::for_snapshot(generation.snapshot()),
&self.shutting_down,
)
})
frontier_sweep.as_ref().is_some_and(|sweep| {
sweep.content_matches(
&self.project_root,
&SourceContentManifestV1::for_snapshot(generation.snapshot()),
&self.shutting_down,
)
})
});
if !retained_empty_seat_settles_source(decoded.is_some(), content_matches) {
return Ok(None);
}
let Some(generation) = decoded else {
return Ok(None);
};
let dirty = {
let hints = self
.hints
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
hints.overflow || !hints.paths.is_empty()
} || configuration_changed;
} || configuration_changed
|| frontier_sweep.is_none()
|| (decoded.is_some() && !content_matches);
if !dirty && !retained_empty_seat_settles_source(decoded.is_some(), content_matches) {
// Quiet + undecoded (or unproven) must not settle: a later wake is
// not a substitute, because this empty-slot seat is the first
// branch and would keep swallowing the content-proof pass.
return Ok(None);
}
if dirty {
self.request_background_reconcile();
}
// Graph prepare decodes without the scheduler mutex. Joining
// `load_active_shared` here parked remount on the publication
// barrier while activation owned it, so the seated event never
// published and the dirty successor extract never started.
self.adopt_ignored_source_roster(&generation);
let snapshot_content_identity = generation.snapshot().content_identity.clone();
let snapshot_content_identity = if let Some(generation) = decoded {
self.adopt_ignored_source_roster(&generation);
generation.snapshot().content_identity.clone()
} else {
ContentDigest::new(pointer.snapshot_content_identity.clone())
.map_err(|error| CodeIndexSchedulerErrorV1::Identity(error.to_string()))?
};
self.latest_content_identity = Some(snapshot_content_identity.clone());
Ok(Some(CodeIndexReconcileOutcomeV1::Noop(
CodeIndexNoopEvidenceV1 {
Expand Down Expand Up @@ -3787,8 +3794,9 @@ fn changed_paths_between_trees(
/// A quiet empty-slot seat may end the pass only when the generation was
/// decoded and its sealed file digests still match the bytes on disk.
///
/// Equal stat metadata with no decoded generation is not currency. Callers
/// that treat `false` as a settled Noop will skip the content proof.
/// Equal stat metadata with no decoded generation is not currency. Dirty
/// remount seating bypasses this gate and may emit a Noop from the durable
/// pointer without joining the publication decode barrier.
pub(crate) fn retained_empty_seat_settles_source(
generation_decoded: bool,
content_matches: bool,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,6 @@ impl CodeIndexSchedulerRegistryV1 {
generation_recovery,
build_progress,
hints,
pending_wake,
source_freshness,
graph_activation_enabled,
) = {
Expand All @@ -295,7 +294,6 @@ impl CodeIndexSchedulerRegistryV1 {
Arc::clone(&worktree.generation_recovery),
Arc::clone(&worktree.build_progress),
Arc::clone(&worktree.hints),
Arc::clone(&worktree.pending_wake),
worktree.source_freshness.clone(),
worktree.graph_activation.policy().is_enabled(),
)
Expand All @@ -318,13 +316,12 @@ impl CodeIndexSchedulerRegistryV1 {
}
progress
});
let refresh_in_flight = reconcile_in_progress.load(Ordering::Acquire) != 0
|| pending_wake
.state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.micros
!= 0;
// `Verifying` / `Refreshing` name an executing source proof or
// rebuild pass. A bare pending wake is only a scheduled follow-up;
// counting it here flipped Fresh→Verifying between consecutive
// status reads after a settled seat (registry publication feeds).
// The pass guard (`reconcile_in_progress`) is the durable signal.
let refresh_in_flight = reconcile_in_progress.load(Ordering::Acquire) != 0;
let source_change_pending = source_freshness.source_change_pending();
let parked = convergence_park
.read()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1509,7 +1509,25 @@ async fn wait_for_dashboard_ready(registry: &CodeIndexSchedulerRegistryV1, path:
&& freshness.coverage == tracedecay_contracts::code_index_freshness::CodeIndexFreshnessCoverageV1::Complete
});
if ready {
break;
// Fresh is projected whenever refresh_in_flight is briefly
// false between owner passes. Join the seating pass and
// re-sample so ready is not a trough before Verifying.
wait_for_quiescent_owner_pass(registry, path).await;
let still_ready = registry
.dashboard_freshness(path)
.await
.is_some_and(|freshness| {
freshness.staleness_state
== Some(
tracedecay_contracts::code_index_freshness::CodeIndexStalenessStateV1::Fresh,
)
&& freshness.coverage
== tracedecay_contracts::code_index_freshness::CodeIndexFreshnessCoverageV1::Complete
});
if still_ready && !registry.reconcile_in_progress_for_test(path).await {
break;
}
continue;
}
tokio::time::sleep(Duration::from_millis(2)).await;
}
Expand Down
Loading