feat: change_impact — transitive blast radius, affected tests, and confidence #22

Closed
opened 2026-07-20 22:37:47 +02:00 by buildagent · 1 comment
Member

Product

One trust-aware call answering “what is affected by this symbol or change-set, and what should I test?”

This supersedes the narrower split between the original blast-radius issue and #23. Both are projections of the same reverse-reachability engine and should share traversal, role classification, confidence accounting, pagination, and limits.

Inputs

  • one or more symbol IDs
  • a file/range
  • the current working-tree or staged diff (reuse changed_symbols)
  • optional maximum depth / production-only / tests-only filters

Output

  • transitive callers grouped by depth, file, crate, and production/test role
  • shortest explanatory path from each seed to important affected roots
  • affected named tests and ready-to-run test filters
  • changed symbols with no statically reachable tests
  • affected public/boundary symbols
  • graph-coverage/confidence metadata: unresolved refs encountered, resolution rate in consulted files, truncation, and explicit “absence is not proof” warnings
  • concise mode suitable for agent preflight; detailed mode for audits

Why one tool

The expensive and correctness-sensitive part is identical: bounded reverse closure over symbol_edges plus role-aware classification. Splitting impact and test selection creates duplicated traversal semantics and makes agents compose two answers that may disagree.

Direct callers are materially misleading in this repository: segment_is_test_dir has 1 direct versus ~195 transitive callers; qualified_name_separator 1/~191; migration run_impl 2/~209. Conversely, seeding apply_resolution selects 16 of 47 Rust test files, a ~66% suite reduction.

Test classification MUST use reference role bit 32 rather than paths: inline #[cfg(test)] mod tests functions live under src/.

Implementation

  • one bounded recursive CTE over the materialized symbol-edge graph
  • visited-set semantics; deterministic shortest-depth result
  • aggregate edge weights and retain at least one shortest witness path
  • join refs/containing symbols for named test functions and role classification
  • seed diff mode from changed_symbols, including deletions from the base side
  • enforce row/depth/time caps and report every truncation honestly

Acceptance

  • symbol and diff inputs produce deterministic results
  • production/test split is role-based and covers inline tests
  • results include executable test filters plus uncovered seeds
  • cycles terminate safely; duplicate paths do not duplicate affected symbols
  • confidence/coverage and truncation are first-class response fields
  • documented semantics for unresolved dynamic/framework dispatch
  • benchmarks cover a high-fan-in seed and a wide multi-symbol diff
  • #23 can close as merged into this issue
## Product One trust-aware call answering **“what is affected by this symbol or change-set, and what should I test?”** This supersedes the narrower split between the original blast-radius issue and #23. Both are projections of the same reverse-reachability engine and should share traversal, role classification, confidence accounting, pagination, and limits. ### Inputs - one or more symbol IDs - a file/range - the current working-tree or staged diff (reuse `changed_symbols`) - optional maximum depth / production-only / tests-only filters ### Output - transitive callers grouped by depth, file, crate, and production/test role - shortest explanatory path from each seed to important affected roots - affected named tests and ready-to-run test filters - changed symbols with no statically reachable tests - affected public/boundary symbols - graph-coverage/confidence metadata: unresolved refs encountered, resolution rate in consulted files, truncation, and explicit “absence is not proof” warnings - concise mode suitable for agent preflight; detailed mode for audits ## Why one tool The expensive and correctness-sensitive part is identical: bounded reverse closure over `symbol_edges` plus role-aware classification. Splitting impact and test selection creates duplicated traversal semantics and makes agents compose two answers that may disagree. Direct callers are materially misleading in this repository: `segment_is_test_dir` has 1 direct versus ~195 transitive callers; `qualified_name_separator` 1/~191; migration `run_impl` 2/~209. Conversely, seeding `apply_resolution` selects 16 of 47 Rust test files, a ~66% suite reduction. Test classification MUST use reference role bit 32 rather than paths: inline `#[cfg(test)] mod tests` functions live under `src/`. ## Implementation - one bounded recursive CTE over the materialized symbol-edge graph - visited-set semantics; deterministic shortest-depth result - aggregate edge weights and retain at least one shortest witness path - join refs/containing symbols for named test functions and role classification - seed diff mode from `changed_symbols`, including deletions from the base side - enforce row/depth/time caps and report every truncation honestly ## Acceptance - symbol and diff inputs produce deterministic results - production/test split is role-based and covers inline tests - results include executable test filters plus uncovered seeds - cycles terminate safely; duplicate paths do not duplicate affected symbols - confidence/coverage and truncation are first-class response fields - documented semantics for unresolved dynamic/framework dispatch - benchmarks cover a high-fan-in seed and a wide multi-symbol diff - #23 can close as merged into this issue
buildagent changed title from feat: impact / blast_radius tool — transitive reverse closure with prod/test split to feat: change_impact — transitive blast radius, affected tests, and confidence 2026-07-20 22:44:36 +02:00
Author
Member

Shipped in v0.5.20 (I029 Track C) as the change_impact MCP tool.

Per-seed bounded reverse closure over symbol_edges (depth ≤ 20, 2000-node cap, ≤ 64 seeds — excess rejected with a structured error, never silently dropped). Production/test split via refs.roles bit 32 with span containment — pinned by a mutation-killing test where a bit-32 ref in a src/ path must classify test and a ref-less symbol in a test path must hit the (previously dead, review-caught) path fallback. Affected named tests + test_files run-set, uncovered_seeds, shortest witness paths to boundary symbols, symbol_edges.weight exposed, and the confidence block (resolution_rate_pct, unresolved_refs_in_scope, stale_edges).

Truncation honesty per the spec: truncated = closure-level cuts only (counts genuinely under-report); a shortened page sets affected_truncated with complete counts; limit=0 = count-only (D6 convention).

Diff input ships as documented composition (changed_symbols → collect ids → symbol_ids) — deterministic, and the 430-line git path stays single-owner; a native diff_base param remains open as a follow-up if composition proves insufficient in practice.

Validated on this repository: segment_is_test_dir → 206 transitive callers (20 prod / 186 test) vs 1 direct — the ~100× under-report this tool exists to fix; apply_resolution selects 19 test files with zero uncovered seeds. Per #22's acceptance, #23 is closed as merged here.

Shipped in **v0.5.20** (I029 Track C) as the `change_impact` MCP tool. Per-seed bounded reverse closure over `symbol_edges` (depth ≤ 20, 2000-node cap, ≤ 64 seeds — excess rejected with a structured error, never silently dropped). Production/test split via `refs.roles` bit 32 with span containment — pinned by a mutation-killing test where a bit-32 ref in a `src/` path must classify test and a ref-less symbol in a test path must hit the (previously dead, review-caught) path fallback. Affected named tests + `test_files` run-set, `uncovered_seeds`, shortest witness paths to boundary symbols, `symbol_edges.weight` exposed, and the confidence block (`resolution_rate_pct`, `unresolved_refs_in_scope`, `stale_edges`). Truncation honesty per the spec: `truncated` = closure-level cuts only (counts genuinely under-report); a shortened page sets `affected_truncated` with complete counts; `limit=0` = count-only (D6 convention). Diff input ships as documented composition (`changed_symbols` → collect ids → `symbol_ids`) — deterministic, and the 430-line git path stays single-owner; a native `diff_base` param remains open as a follow-up if composition proves insufficient in practice. Validated on this repository: `segment_is_test_dir` → 206 transitive callers (20 prod / 186 test) vs 1 direct — the ~100× under-report this tool exists to fix; `apply_resolution` selects 19 test files with zero uncovered seeds. Per #22's acceptance, #23 is closed as merged here.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
h-dv/code-index#22
No description provided.