fix(code-index): diagnose graph-rebuild timeout and withhold a lagging seat - #1557
Merged
ScriptedAlchemy merged 1 commit intoSep 17, 2026
Merged
Conversation
Status advertised the replacement text owner as current before the serving swap, so search kept answering the predecessor and bound a retryable graph generation mismatch. Terminal freshness now requires the advertised generation and the serving seat to be the same. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
|
ScriptedAlchemy
changed the base branch from
codex/tracedecay-total-redesign-plan-reopened
to
master
September 17, 2026 22:38
ScriptedAlchemy
marked this pull request as ready for review
September 17, 2026 22:38
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
2 of 3 tasks
Contributor
Performance Comparison
|
This was referenced Sep 17, 2026
ScriptedAlchemy
added a commit
that referenced
this pull request
Sep 17, 2026
…1569) 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.
4 of 5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
graph_rebuild_status_test::background_refresh_and_reopen_report_only_servable_generations, which fails both nextest attempts at the 90s receipt (crates/tracedecay/tests/transport_acceptance_suite/graph_rebuild_status_test.rs:176).7bdc33d3cbis not the failure. Both receipts have truncation counters at 0.Targets
codex/tracedecay-total-redesign-plan-reopened(PR #707). Does not touchmain/master.Evidence
Linux root-transport nextest report from CI run 35258386278 (commit
11ab3edc20, after the limit=3 tweak). Artifactnextest-junit-Linux-root-transport. Both attempts panic at line 176 (timed out waiting for current generation). Temp roots differ (/tmp/.tmpXiBdAXvs/tmp/.tmpOM98kG).current/complete/freshstale/partial_source_verification/verifyingsearch=null; completedtracedecay_searchstayed at 2)…00000001, latest…00000002;results: []verified-code-graph-generation-mismatch(retryable)0/23051024/2305last_complete_stale,edge_count: 0,symbol_count: 98305current, same countscode_graph_servingreadyreadyready, 769/769 files,rebuild_in_flight: falsegraph_superseded_replay_retirement_failed: parent/tmp/.tmpXiBdAX/profile/projects/proj_94c986a0cfdce935missing (os error 2), stagepublish, ~1s before the panicsymbol_count98305 is768*128+1.edge_count0 iscollect_edge_evidenceover functions that do not call each other (crates/tracedecay-code-index/src/production/mod.rs:2168), not a missing projector. Rejected as a cause.Ranked causes
Serving seat lags the text owner that status advertises (shared cause). A publish writes the replacement text owner into
worker_text_generationbefore the serving swap (crates/tracedecay-code-index-runtime/src/code_index_scheduler/registry/mount.rs:1059-1087). Freshness identity is taken from that text owner (serving_reads.rs,dashboard_text_freshness_identity).dashboard_generation_is_readytreated ready text lanes plusCodeGraphServingReadinessV1::Readyas terminal, without comparing seat ids. Search executes the serving slot and, when lanes are stale, reportsCodeIndexLaneStatusV1::Stale(query_runtime.rs:385-388). The verified graph is already the head, sobind_verified_graph_to_searchreturnsverified-code-graph-generation-mismatch(crates/tracedecay-mcp/src/handlers/graph/search_evidence.rs:61-76). Attempt 2 did this on ~32 searches. Attempt 1 only becamecurrentas the 90s window expired, so the search await was cancelled (search=null). This PR stops calling that splitcurrent. It does not move the seat.The swap is skipped while graph activation or text projection is unfinished (why 90s is not enough). Retryable graph activation clears the seat candidate (
mount.rs:1641-1671:result = Ok((Err(error), None, None)), “stays unseated until the scheduled retry”). AnUnfinishedtext projection also clearslatestso the swap predicate fails (mount.rs:1775-1786). Clone-fingerprint backfill is deliberately not started until the seat matches the text owner (mount.rs:590-611). The fixture is 768 files × 128 functions: 2305 clone pages, ~645–699 MiB,completed_lexical_units455672760. Attempt 1 never left page 0; attempt 2 reached 1024/2305 and still served the predecessor. Not fixed here.Mismatch is a typed symptom, not a second root.
race_primary_search_with_graphwaits for the graph on a sparse page (search.rs:238-244,should_check_external_import_hintis true whenresult_count < limit). Binding then refuses a newer graph. Emptyresultsis becauserefresh_probe_0000_000is not in generation 1, not because MCP truncated the page.Page-limit / MCP truncation — rejected.
7bdc33d3cbdroppedlimitfrom 20 to 3. This CI run hasirreversible_truncation_total: 0,store_attempts: 0,bytes_before_truncation_total: 0. Attempt 2 returned a full JSON body.Missing graph-db parent — attempt 1 only, not the timeout.
retire_superseded_replayslogs and swallowsGraphDbError::Unavailable(crates/tracedecay-store-runtime/src/session_registry/code_graph.rs:594-601; resolve atcrates/tracedecay-graph-db/src/registry/path.rs:64-69). Different temp dir, absent from attempt 2, timestamped at teardown. Do not treat it as shared side effect. Nextestretries = 1starts a new process (.config/nextest.toml).Error taxonomy
Code-graph projection (
CodeGraphProjectionError,graph_projection.rs:71)reconcile.rs:291)graph-query/src/projection.rs:256)map_code_graph_read_runtime_error)ContractInvalidRequestcode-graph-invalid-requestGenerationMismatchStalecode-graph-stale(retryable)CancelledCancelledcode-graph-cancelledBudgetExhaustedis_graph_activation_refusal)BudgetExhaustedcode-graph-budget-exhaustedDeadlineExceededTimedOutcode-graph-timed-out(retryable)Conflictactivation_conflict_context)Unavailablecode-graph-unavailable(retryable)ProjectionMismatch/RecoveredGenerationMismatchcode-graph-corrupt/code-graph-staleResetRequiredResetRequiredCorruptCorruptcode-graph-corruptUnavailableUnavailablecode-graph-unavailableDurabilityUncertain/ClosedClosedis retryableUnavailablecode-graph-unavailableGraphDbError(graph-db/src/error.rs:88) maps into those variants inCodeGraphProjectionError::from. Extra graph-db variantsSourceCommitmentsUnavailableandSealedRevisionIncompatiblebecomeUnavailableand are not retryable activation.Search / status surfaces (what the 90s test actually sees)
verified-code-graph-generation-mismatchsearch_evidence.rs:67, used byhandle_search(search.rs:294-296) and the context path (search.rs:814)verified-code-graph-read-unavailablesearch_evidence.rs:50when primary search finishes first and the graph is not requiredcurrent/warming/stale/parkedhandlers/info/status.rs:451current; attempt 2stale+ warning “verifies source freshness”last_complete_stale+source_verificationstatus.rs:284-286ServingSwapOutcomeV1Seated/SeatedStale/Superseded/Offeredregistry.rsdecideGraphActivationGateV1Activate/AlreadyServing/UnchangedGraph/PendingAttemptSpentregistry.rs:272tracedecay-cli/src/status_cmd.rsdecodes the samegraph_statistics+code_index_freshness;commands/index.rscallstracedecay_admin_synctransport_acceptance_suite/graph_rebuild_status_test.rsfact_store/curation/tests.rsgraph_rebuild_rejects_a_dangling_canonical_link_eventis a different store and not this timeoutDaemon path is the scheduler worker in
registry/mount.rs, entered bytracedecay_admin_sync(status: queuedin the receipt). MCP search ishandle_search. There is no separate CLI graph-rebuild subcommand.Test plan
scripts/require-exact-test.sh cargo test -p tracedecay-code-index-runtime --lib -- graph_rebuild_split_is_not_terminal_freshness dashboard_ready_requires_text_and_graph_lane_owners— 2 passed.background_refresh_and_reopen_report_only_servable_generationswas not re-run. CI already shows it red at 90s both attempts. This PR does not install the serving seat, so that journey is still expected to time out. Do not raiseRECEIPT_TIMEOUT.Next fix PRs (not this change)
mount.rs:1641-1671drops the swap candidate on retryable graph activation. Exact/lexical should seat, and graph activation should retry beside that seat. Today the comment at1582says the swap still installs, then the error arm deletes the candidate.mount.rs:1704-1718states that intent;1775still clears the seat). The 2305-page backfill is the wall-clock cost. Measure it before changing any budget.bind_verified_graph_to_searchstrict. If the seat is current, the mismatch stops happening. Do not special-case the probe query.graph_superseded_replay_retirement_failedon a missingprojects/proj_<prefix>directory is a swallowedUnavailable. Confirm whether that parent is never created or is dropped during shutdown. Do not conflate it with the timeout.Checklist
warmingwhile the text owner is ready and the serving slot is still the predecessor. That is the corrected contract.