Skip to content
Merged
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 @@ -1116,11 +1116,15 @@ pub(super) fn dashboard_code_graph_serving(
))
}

/// Whether status may report this worktree as terminal (`fresh` / `current`).
/// Whether the lane owners alone would let status report this worktree as
/// terminal (`fresh` / `current`).
///
/// Refused graph activation remains terminal for text serving, preserving the
/// existing status behavior; strict dogfood can distinguish it from Ready via
/// the separate typed projection.
/// the separate typed projection. Lane readiness is not seat identity: a
/// publication installs the replacement text owner before the serving swap
/// (`registry/mount.rs`), and search still answers the incumbent seat.
/// [`dashboard_terminal_status`] is what freshness reads use.
fn dashboard_generation_is_ready(
latest: Option<&LatestCompleteCodeIndexV1>,
text_ready: bool,
Expand All @@ -1134,6 +1138,61 @@ fn dashboard_generation_is_ready(
}
}

/// Whether the generation status advertises is the one search will serve.
///
/// `advertised_text_generation_id` is `None` when no text owner is installed;
/// freshness then names the serving seat, so there is no split to refuse.
pub(super) fn serving_seat_matches_advertised_generation(
serving_generation_id: Option<&str>,
advertised_text_generation_id: Option<&str>,
) -> bool {
match advertised_text_generation_id {
None => true,
Some(advertised) => serving_generation_id == Some(advertised),
}
}

pub(super) fn serving_seat_matches_advertised_text_owner(
serving: Option<&LatestCompleteCodeIndexV1>,
text: Option<&LatestCodeTextGenerationV1>,
) -> bool {
serving_seat_matches_advertised_generation(
serving.map(|serving| serving.generation().manifest().generation_id.as_str()),
text.map(|text| text.metadata().manifest().generation_id.as_str()),
)
}

/// Terminal freshness: lane owners are ready and the serving seat is the
/// generation status will advertise.
pub(super) fn dashboard_terminal_status(
latest: Option<&LatestCompleteCodeIndexV1>,
text: Option<&LatestCodeTextGenerationV1>,
text_ready: bool,
graph_activation_enabled: bool,
code_graph_serving: &Option<CodeGraphServingReadinessV1>,
) -> bool {
terminal_status_from_lanes(
dashboard_generation_is_ready(
latest,
text_ready,
graph_activation_enabled,
code_graph_serving,
),
serving_seat_matches_advertised_text_owner(latest, text),
)
}

/// Lane readiness and seat identity are one freshness verdict.
///
/// The graph-rebuild receipt had ready lanes and a serving seat that still
/// held the predecessor. That combination is not terminal.
pub(super) fn terminal_status_from_lanes(
lanes_ready: bool,
serving_matches_advertised: bool,
) -> bool {
lanes_ready && serving_matches_advertised
}

fn dashboard_text_freshness_identity(
latest: Option<&LatestCodeTextGenerationV1>,
) -> tracedecay_contracts::code_index_freshness::CodeIndexWorktreeFreshnessV1 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use tracedecay_domain::ProjectId;
use super::super::graph_activation::install_injected_activation_gate;
use super::{
CodeIndexCadenceOutcomeV1, CodeIndexSchedulerRegistryV1, dashboard_generation_is_ready,
serving_seat_matches_advertised_generation, terminal_status_from_lanes,
};

/// Failure bound on an owner pass finishing once the worker is parked. Nothing
Expand Down Expand Up @@ -52,6 +53,45 @@ fn dashboard_ready_requires_text_and_graph_lane_owners() {
}
}

/// The graph-rebuild receipt (transport acceptance, both ~90s attempts): lane
/// owners were ready on the replacement text generation while search still
/// served the predecessor. Status must not call that split terminal.
#[test]
fn graph_rebuild_split_is_not_terminal_freshness() {
let lanes_ready =
dashboard_generation_is_ready(None, true, true, &Some(CodeGraphServingReadinessV1::Ready));
assert!(
lanes_ready,
"the receipt's text owner and graph activation were ready"
);
assert!(
!serving_seat_matches_advertised_generation(
Some("generation.predecessor"),
Some("generation.head"),
),
"search served the predecessor while status advertised the head"
);
assert!(
!serving_seat_matches_advertised_generation(None, Some("generation.head")),
"a text owner installed before the serving swap is not yet the served generation"
);
assert!(serving_seat_matches_advertised_generation(
Some("generation.head"),
Some("generation.head"),
));
assert!(serving_seat_matches_advertised_generation(
Some("generation.head"),
None,
));
assert!(serving_seat_matches_advertised_generation(None, None));
assert!(
!terminal_status_from_lanes(lanes_ready, false),
"ready lanes must not be terminal while search serves the predecessor"
);
assert!(terminal_status_from_lanes(lanes_ready, true));
assert!(!terminal_status_from_lanes(false, true));
}

/// Park the background worker and wait out whatever pass is already in flight.
///
/// The worker releases its admission permit after source reconciliation but
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use super::scope_identity::{latest_matches_scope_identity, text_matches_scope_id
use super::{
CodeIndexMountedScopeV1, CodeIndexSchedulerRegistryV1, CodeIndexServingScopeV1,
MountedCodeIndexWorktreeV1, PendingWakeClaimV1, ReadyProbeServingPartsV1,
dashboard_code_graph_serving, dashboard_freshness_identity, dashboard_generation_is_ready,
dashboard_code_graph_serving, dashboard_freshness_identity, dashboard_terminal_status,
dashboard_text_freshness_identity, unique_mounted_for_scope,
};

Expand Down Expand Up @@ -369,8 +369,9 @@ impl CodeIndexSchedulerRegistryV1 {
&text.metadata().manifest().generation_id,
)
});
let ready = dashboard_generation_is_ready(
let ready = dashboard_terminal_status(
latest.as_ref(),
text.as_ref(),
text_ready,
graph_activation_enabled,
&code_graph_serving,
Expand Down Expand Up @@ -443,8 +444,9 @@ impl CodeIndexSchedulerRegistryV1 {
&text.metadata().manifest().generation_id,
)
});
let ready = dashboard_generation_is_ready(
let ready = dashboard_terminal_status(
latest.as_ref(),
text.as_ref(),
text_ready,
graph_activation_enabled,
&code_graph_serving,
Expand Down
Loading