Skip to content

test(code-index): no-call graph rebuild seals zero edges - #1572

Draft
ScriptedAlchemy wants to merge 3 commits into
masterfrom
cursor/graph-rebuild-edge-census-58ff
Draft

ScriptedAlchemy wants to merge 3 commits into
masterfrom
cursor/graph-rebuild-edge-census-58ff

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Summary

  • edge_count 0 with symbol_count 98305 is the sealed relation census of the no-call refresh fixture, not a stalled graph projector.
  • The Mac timeout already had that census on the matched generation. A census is only Observed after the graph store is installed, which happens after the full snapshot. The projector had finished.
  • Do not change edge emission, and do not raise the 90s RECEIPT_TIMEOUT, in fix(code-index): seat text through retryable graph activation #1562. This follow-up only records the fixture shape.

Citations below are from PR #1562 tip b80dd58 unless the path is unchanged on master (the seat commit does not touch emission). registry.rs and mount.rs line numbers are the seat tip.

Verdict

Expected. Not a stalled projector. Incremental edge emission would not move this receipt.

The status field is graph_statistics.edge_count, which is CodeIndexGenerationStatisticsV1.edge_count: the count of CanonicalRelationEdgeV1 rows sealed with the generation (crates/tracedecay-code-index/src/production/mod.rs:2172-2176, generation_statistics.rs:44-61). It is not a live graph-db relation counter and it does not include CodeFileContainsSymbol projector rows (crates/tracedecay-code-index/src/graph_projection.rs:59).

98305 = 768 files × 128 functions + before_reopen. That is one symbol per function (chunks.rs test now locks the one-function shape). A partial emit would not land on that exact total while still claiming zero call edges and a finished clone/text phase. The number is the sealed generation, not a counter stuck mid-projection.

Why this fixture seals zero relation edges

The batch is:

pub fn refresh_probe_{file:04}_{symbol:03}(input: u32) -> u32 { input + N }

(crates/tracedecay/tests/transport_acceptance_suite/graph_rebuild_status_test.rs:232-237). No calls, no imports, no impls, no modules.

  1. The Rust extractor pushes the file node as the parent (crates/tracedecay-code-extraction/src/rust_extractor.rs:166-168) and emits Contains from that parent to each function (280-287).
  2. File nodes are not symbol rows (crates/tracedecay-code-index/src/chunks.rs:773-786). canonical_relation_edges abstains those edges as MissingSymbolEndpoint (2497-2502). The existing comment at 4250-4251 states this: the file node is not a symbol row, so its Contains edges always abstain.
  3. Parameter and return u32 become unresolved TypeOf / Returns refs (rust_extractor.rs:296-315, 2034-2042). Same-file resolution finds no symbol named u32. Cross-file resolution returns immediately for a bare name with no import and no glob (crates/tracedecay-code-index/src/production/helpers.rs:560-568). Those refs never become edges.
  4. collect_edge_evidence only concatenates per-file edges plus cross-file resolutions (helpers.rs:358-371). Both are empty. edges.len() is the census.

A same-file call still seals a Calls edge. The emitter is live. The proof is chunks::tests::no_call_refresh_probe_seals_zero_relation_edges (chunks.rs:4282). Ran:

cargo test -p tracedecay-code-index --lib chunks::tests::no_call_refresh_probe_seals_zero_relation_edges -- --exact

1 passed, 0 failed.

Projector gate, and why it is not this timeout

Graph activation does wait on a full structural backfill before Ready, even when relation edges are empty.

  • publish_verified_snapshot builds the whole manifest before any head exists (crates/tracedecay-store-runtime/src/session_registry/code_graph.rs:1233, manifest at 1329-1346).
  • build_projection allocates every file entity, every symbol entity, and every CodeFileContainsSymbol relation, then returns (crates/tracedecay-code-index/src/graph_projection/builder.rs:286-384). Symbol hashing is windowed (332-340, workers × 512) but the function does not publish a prefix.
  • activate_persistent_graph calls install_graph_serving only after that snapshot returns (crates/tracedecay-code-index-runtime/src/code_index_scheduler/graph_activation.rs:734-774). Ready is that install (serving.rs:3508-3512). Catalog warm is already background and is not the Ready gate (graph_activation.rs:756-758).

For this fixture the rows are about 769 file entities, 98,305 symbol entities, and 98,305 file-contains-symbol relations. The call-edge loop (builder.rs:375-384) is empty. Those containment rows are invisible to edge_count.

The census reader only reports statistics after interactive_graph_store() succeeds (crates/tracedecay-code-index-runtime/src/project_reads.rs:102-119 and 159-174). That store exists only in CodeGraphActivationStateV1::Ready (serving.rs:1993-2011, 2062-2074). An Observed census with symbol_count 98305 means the full snapshot was already installed. edge_count 0 is the sealed answer sitting on a finished projector, not a counter waiting for edges that will never arrive.

progress.phase = ready is the text artifact phase, published when lexical owners install (serving.rs:3206-3211). A clone successor can still start after that. Clone pages 2305/2305 are that successor's source-page observation, not relation emission. Neither input is edge_count.

Ranked proposals

  1. Do not land an emission change in fix(code-index): seat text through retryable graph activation #1562. Do not raise RECEIPT_TIMEOUT. Zero relation edges is the fixture. Changing the projector or fabricating containment edges into the census would lie about the source. The proof test belongs in this follow-up, not in the seat-swap PR.

  2. Do not stream relation edges to save this receipt. Follow-up only if a different journey measures code_index.seal.collect.emit. There are no relation edges to stream. Streaming the 98k symbol / file-symbol windows would still leave freshness non-current until the last window, because dashboard_terminal_status requires code_graph_serving == Ready (registry.rs:1205-1231 on b80dd58) and Ready is the full snapshot. Estimated speedup on this 90s receipt: none. The dump already showed the finished census. Loosening Ready to the first 512-row window would report a partial graph as terminal; that is not a valid cut.

  3. Optional, low, not this timeout: status copy says "graph counts are not authoritative" while warming (crates/tracedecay-mcp/src/handlers/info/status.rs:482-487). That sentence invites reading edge_count 0 as unfinished work. A separate wire field for CodeFileContainsSymbol would stop the misread, but it is a contract change and does not make wait_for_current_generation return. Skip unless an operator surface is actually wrong.

What the receipt is still waiting on

Not this lane, so the next pass does not re-litigate edges.

wait_for_current_generation (graph_rebuild_status_test.rs:153-169) needs freshness status current. That is coverage complete and staleness fresh (status.rs:451-456). The ladder (crates/tracedecay-contracts/src/code_index_freshness.rs:426-466) does not read edge_count. After the seat matches and the graph is Ready, the remaining inputs are reconcile_in_progress, source_verified != Some(false), and hook_hint_count == Some(0), plus source_reference / source_revision.

On b80dd58 the public pass guard is dropped before native graph activation (mount.rs:1159-1173), so a long snapshot does not itself hold rebuild_in_flight. The Mac dump already had seat match, search hits, retrieval serving, age ≈ 89s, clone pages complete, and text phase ready. The next field to read off that same status object is staleness_state, coverage, rebuild_in_flight, and hook_hint_count — not the projector.

Test plan

  • cargo test -p tracedecay-code-index --lib chunks::tests::no_call_refresh_probe_seals_zero_relation_edges -- --exact — 1 passed
  • No timeout or budget change
  • Did not re-run background_refresh_and_reopen_report_only_servable_generations (the receipt under diagnosis; this change does not alter it)

Checklist

  • No secrets
  • CHANGELOG not updated (test-only proof of an existing census shape)
  • No timeout change
Open in Web Open in Cursor 

The graph-rebuild refresh fixture emits file Contains edges that abstain
and primitive u32 refs that never bind. Census edge_count 0 is that
shape, not a stalled projector.

Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
@changeset-bot

changeset-bot Bot commented Sep 17, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 11b3cc9

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants