Graph snapshot diff (read path)
Structural comparison between two columnar v2 snapshots for PR gates.
Layers
| Layer | Module | Role |
|---|---|---|
| L0 | snapshot_diff::SnapshotPair | Parallel open; digest fast path |
| L1 | stable_key::StableNodeKey | BLAKE3(path, name, type) — UUID-independent |
| L2 | snapshot_diff | DiffSink, node hash index, edge merge-join on stable keys |
| L3 | rgctl_incremental::pr_scope | Git name-status paths + hunk line overlap |
| L3b | rgctl_incremental::cascade | Reverse Calls expansion for caller re-extract |
| L4 | pr-check CLI | Temporal policy on scoped head entities |
| L5 | policy_diff | NEW / EXISTING / RESOLVED classification |
| L6 | check --base-ref/--head-ref/--strict | Working-tree / PR diff scoping fixes |
Hot-path rules
- Mmap-only string pool reads via
string_at(noStringalloc on keys). - Edge diff: map UUID →
StableNodeKey, sort stable edge keys, merge-join (notedge_meta: Veccollect). - Node index on head: rayon shard by
idx % 16, merge disjoint maps. - Sync CPU; async hosts use
spawn_blockingat the boundary only.
Benchmarks
cargo bench -p rgctl-graph --bench snapshot_diff
Groups: digest_fast_path_equal, node_index_parallel, edge_merge_join, full_diff_noop_sink.
Reverse-dependency cascade
When a callee file changes (especially renames), call sites in untouched caller files must be
re-parsed so Calls edges target the new stable node IDs.
ChangeSet.invalidation_paths()yields modified/deleted/rename-old paths.incoming_callers_files_depth(base_mmap, seeds, N)scans incomingCallsedges into nodes declared in those files and returns caller declaring files.IncrementalUpdaterunions cascaded paths withextract_paths()before delta extract +rebuild_relations.
--cascade-depth (default 1, 0 = disabled) on incremental update and pr-check (used when
delta head synthesis lands in Phase 4).
Scoped policy analysis (Phase 5)
pr-check no longer dual-hydrates full graphs for blast/centrality:
- Resolve scoped entity UUIDs from
StableNodeKeyon each snapshot. collect_upstream_call_closure→ mmapCallsreverse BFS.hydrate_subset→ smallMemoryBackendfor policy node lookups.BlastRadiusEngine::build_scopedon the upstream closure (parity with full graph for scoped seeds).- Centrality: reuse
.rgctl/analysis_results.binwhen node count matches; elseanalyze_scopedon seed entities only.