From 0ef97d2186f04b3c2db31c3441b50092bc457587 Mon Sep 17 00:00:00 2001 From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com> Date: Thu, 17 Sep 2026 16:44:06 -0700 Subject: [PATCH] fix(code-index): keep graph-off text serving terminal without a seat Forward-port of #1566 from codex/tracedecay-total-redesign-plan-reopened, which is no longer the integration branch. The seat-identity check from #1557 compared the decoded serving slot with the advertised text owner for every mount. A graph-off mount serves its text owner directly and never seats the decoded generation, so status could never reach fresh. Apply the check only when graph activation is enabled, where the decoded seat is what search serves, and fold the one-line wrappers into dashboard_terminal_status. --- .../src/code_index_scheduler/registry.rs | 61 +++++++------------ .../registry/serving_readiness_tests.rs | 37 +++++++---- 2 files changed, 46 insertions(+), 52 deletions(-) diff --git a/crates/tracedecay-code-index-runtime/src/code_index_scheduler/registry.rs b/crates/tracedecay-code-index-runtime/src/code_index_scheduler/registry.rs index fc9c8db791..ce9c278e80 100644 --- a/crates/tracedecay-code-index-runtime/src/code_index_scheduler/registry.rs +++ b/crates/tracedecay-code-index-runtime/src/code_index_scheduler/registry.rs @@ -1121,10 +1121,8 @@ pub(super) fn dashboard_code_graph_serving( /// /// 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. 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. +/// the separate typed projection. [`dashboard_terminal_status`] is what +/// freshness reads use. fn dashboard_generation_is_ready( latest: Option<&LatestCompleteCodeIndexV1>, text_ready: bool, @@ -1140,30 +1138,25 @@ 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. +/// Status identity is taken from the text owner, but a publication installs +/// the replacement text owner before the serving swap (`registry/mount.rs`), +/// and a graph-on search answers from the decoded seat. Until the seat catches +/// up, search serves the predecessor and the split is not terminal. A graph-off +/// mount serves the text owner directly and deliberately never seats the +/// decoded generation, so there is no seat to compare. pub(super) fn serving_seat_matches_advertised_generation( + graph_activation_enabled: bool, 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), + Some(advertised) if graph_activation_enabled => serving_generation_id == Some(advertised), + _ => true, } } -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. +/// Terminal freshness: lane owners are ready and search serves the generation +/// status advertises. pub(super) fn dashboard_terminal_status( latest: Option<&LatestCompleteCodeIndexV1>, text: Option<&LatestCodeTextGenerationV1>, @@ -1171,28 +1164,18 @@ pub(super) fn dashboard_terminal_status( graph_activation_enabled: bool, code_graph_serving: &Option, ) -> 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), + dashboard_generation_is_ready( + latest, + text_ready, + graph_activation_enabled, + code_graph_serving, + ) && serving_seat_matches_advertised_generation( + graph_activation_enabled, + latest.map(|latest| latest.generation().manifest().generation_id.as_str()), + text.map(|text| text.metadata().manifest().generation_id.as_str()), ) } -/// 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 { diff --git a/crates/tracedecay-code-index-runtime/src/code_index_scheduler/registry/serving_readiness_tests.rs b/crates/tracedecay-code-index-runtime/src/code_index_scheduler/registry/serving_readiness_tests.rs index d47bf06e5f..9b2094741c 100644 --- a/crates/tracedecay-code-index-runtime/src/code_index_scheduler/registry/serving_readiness_tests.rs +++ b/crates/tracedecay-code-index-runtime/src/code_index_scheduler/registry/serving_readiness_tests.rs @@ -13,7 +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, + serving_seat_matches_advertised_generation, }; /// Failure bound on an owner pass finishing once the worker is parked. Nothing @@ -55,41 +55,52 @@ 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. +/// served the predecessor seat. 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, + dashboard_generation_is_ready(None, true, true, &Some(CodeGraphServingReadinessV1::Ready)), "the receipt's text owner and graph activation were ready" ); assert!( !serving_seat_matches_advertised_generation( + true, 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")), + !serving_seat_matches_advertised_generation(true, None, Some("generation.head")), "a text owner installed before the serving swap is not yet the served generation" ); assert!(serving_seat_matches_advertised_generation( + true, Some("generation.head"), Some("generation.head"), )); assert!(serving_seat_matches_advertised_generation( + true, 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)); + assert!(serving_seat_matches_advertised_generation(true, None, None)); +} + +/// A graph-off mount serves its text owner directly and never seats the +/// decoded generation; the empty seat is not a lagging seat. +#[test] +fn graph_off_text_owner_is_terminal_without_a_seat() { + assert!(serving_seat_matches_advertised_generation( + false, + None, + Some("generation.head"), + )); + assert!(serving_seat_matches_advertised_generation( + false, + Some("generation.predecessor"), + Some("generation.head"), + )); } /// Park the background worker and wait out whatever pass is already in flight.