Skip to content

feat: wire points-to solver max-iterations cap through DEFAULTS.analysis.pointsToMaxIterations#1868

Closed
carlos-alm wants to merge 1 commit into
fix/issue-1752-blast-radius-incremental-stalefrom
fix/issue-1753-points-to-max-iterations
Closed

feat: wire points-to solver max-iterations cap through DEFAULTS.analysis.pointsToMaxIterations#1868
carlos-alm wants to merge 1 commit into
fix/issue-1752-blast-radius-incremental-stalefrom
fix/issue-1753-points-to-max-iterations

Conversation

@carlos-alm

Copy link
Copy Markdown
Contributor

Summary

  • MAX_SOLVER_ITERATIONS = 50 was hardcoded independently in both the WASM (points-to.ts) and native (build_edges.rs) points-to solvers, duplicating DEFAULTS.analysis.pointsToMaxIterations, which was marked @reserved/unwired in both.
  • Threaded maxIterations through buildPointsToMapbuildPointsToMapForFilebuildCallEdgesJS/buildCallEdgesNative, sourced from ctx.config.analysis.pointsToMaxIterations. Native side: added AnalysisConfig to BuildConfig (deserialized from the same JSON payload TS already sends), threaded through build_call_edgesprocess_filebuild_file_contextbuild_pts_map_for_filebuild_points_to_map.
  • Default (50) unchanged on both engines — plumbing only, solver algorithm untouched.

Closes #1753

Test plan

  • npm test — full suite green (3513 passed, 0 failed)
  • npm run lint — clean
  • cargo test — 445 passed
  • Native addon rebuilt, codesigned, verified directly
  • Constructed an 8-hop alias chain requiring exactly 8 solver iterations to converge: cap=3 fails to resolve, cap=8 resolves, default (50) resolves — verified identically on both engines via a full-pipeline dual-engine test with a .codegraphrc.json override
  • Resolution-benchmark precision/recall confirmed byte-for-byte unchanged across all fixtures (javascript 100/100, pts-javascript 100/100, typescript 95.7%/93.6% matching the pre-existing documented baseline exactly, tsx/dynamic-javascript/dynamic-typescript 100/100)

Filed #1867 for an out-of-scope finding: a pre-existing unrelated compiler warning in extractors/javascript.rs.

Stacked on #1866 (base branch fix/issue-1752-blast-radius-incremental-stale) — only the resolver/builder/config/test diff is this issue's change.

…sis.pointsToMaxIterations

MAX_SOLVER_ITERATIONS was a hardcoded 50 in both the WASM points-to solver
(points-to.ts) and the native Rust solver (build_edges.rs), duplicating but
never reading DEFAULTS.analysis.pointsToMaxIterations. Threads a maxIterations
parameter from the pipeline's resolved config through buildPointsToMap ->
buildPointsToMapForFile -> buildCallEdgesJS/buildCallEdgesNative on the TS
side, and through build_call_edges -> process_file -> build_file_context ->
build_pts_map_for_file -> build_points_to_map on the Rust side, sourced from
a new BuildConfig.analysis.points_to_max_iterations field deserialized from
the JSON config payload already passed to the native engine. Default value
(50) is unchanged when no override is configured.

docs check acknowledged: no README/CLAUDE.md/ROADMAP.md updates needed —
purely internal plumbing for an already-documented, already-accepted config
key with no new CLI flags or user-facing surface.

Fixes #1753

Impact: 7 functions changed, 6 affected
@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR wires DEFAULTS.analysis.pointsToMaxIterations (previously @reserved) through both the TypeScript/WASM and native Rust points-to solvers, replacing the duplicated hardcoded constant of 50 in both engines with a value sourced from ctx.config / BuildConfig.analysis. The default behavior (50 iterations) is unchanged.

  • TypeScript/WASM path: buildPointsToMap gains a maxIterations parameter (defaulting to DEFAULTS.analysis.pointsToMaxIterations); buildCallEdgesJS and buildCallEdgesNative pass ctx.config.analysis.pointsToMaxIterations through buildPointsToMapForFile / native.buildCallEdges.
  • Rust native path: A new AnalysisConfig struct is added to BuildConfig (deserialized from the existing JSON payload TS sends), threaded through build_and_insert_call_edgesbuild_call_edgesprocess_filebuild_pts_map_for_filebuild_points_to_map. The old MAX_SOLVER_ITERATIONS constant is now #[cfg(test)]-gated.
  • Tests: New unit tests cover below-depth, at-depth, and default-omitted cases on both TS and Rust sides; a new integration test builds an 8-hop alias chain to verify engine parity and correct suppression/resolution under a .codegraphrc.json override.

Confidence Score: 4/5

Safe to merge; default behavior is byte-for-byte unchanged and both engines are guarded by thorough unit and integration tests.

The plumbing is clean and the algorithm is untouched. The only open gap is that pointsToMaxIterations: 0 in a .codegraphrc.json would silently disable all alias-chain resolution on both engines without any error or warning — now that the value is truly user-configurable, a minimum-value guard at config load time (or a clamp inside the solver) would make misconfiguration more visible.

Both src/domain/graph/resolver/points-to.ts and crates/codegraph-core/src/domain/graph/builder/stages/build_edges.rs iterate over maxIterations, so either a clamp in the config loader or a guard in both solver entry points would close the zero-value gap.

Important Files Changed

Filename Overview
src/domain/graph/resolver/points-to.ts Removes the hardcoded MAX_SOLVER_ITERATIONS constant; adds maxIterations as the final optional parameter of buildPointsToMap (defaulting to DEFAULTS.analysis.pointsToMaxIterations) and threads it into buildCallSiteTypeMap.
src/domain/graph/builder/stages/build-edges.ts Threads ctx.config.analysis.pointsToMaxIterations through both JS (buildCallEdgesJSbuildPointsToMapForFile) and native (buildCallEdgesNativenative.buildCallEdges) paths.
crates/codegraph-core/src/domain/graph/builder/stages/build_edges.rs Adds max_iterations: u32 to build_call_edges, process_file, build_file_context, build_pts_map_for_file, and build_points_to_map; gates MAX_SOLVER_ITERATIONS with #[cfg(test)] for use only in unit tests.
crates/codegraph-core/src/infrastructure/config.rs Adds AnalysisConfig struct (with points_to_max_iterations: u32, default 50) to BuildConfig; uses #[serde(rename_all = "camelCase")] to correctly deserialize pointsToMaxIterations from the TS JSON payload.
crates/codegraph-core/src/domain/graph/builder/pipeline.rs Passes config.analysis.points_to_max_iterations to build_and_insert_call_edges, which forwards it to build_call_edges — completing the native-first pipeline path.
src/infrastructure/config.ts Updates the @reserved comment on pointsToMaxIterations to describe that it is now wired to both solver engines; removes the now-resolved item from the surrounding TODO comment.
src/types.ts Adds maxIterations: number to the NativeAddon.buildCallEdges signature so TypeScript's structural typing matches the updated Rust NAPI export.
tests/integration/issue-1753-points-to-max-iterations.test.ts New integration test: builds an 8-hop alias chain, verifies default cap resolves it on WASM, cap=3 suppresses it on both engines, cap=8 restores it on WASM, and both engines agree on parity.
tests/unit/config.test.ts Adds a unit test verifying that loadConfig reads analysis.pointsToMaxIterations from .codegraphrc.json while preserving sibling defaults.
tests/unit/points-to.test.ts Adds three maxIterations unit tests: below-depth cap doesn't converge, at-depth cap does converge, and omitting the argument falls back correctly to the default (50).

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[".codegraphrc.json\nanalysis.pointsToMaxIterations"] -->|loadConfig TS| B["ctx.config.analysis\n.pointsToMaxIterations"]
    A -->|serde JSON| C["BuildConfig.analysis\n.points_to_max_iterations"]

    B -->|JS path| D["buildCallEdgesJS()"]
    B -->|Native JS-orchestrated| E["buildCallEdgesNative()\nnative.buildCallEdges(..., maxIterations)"]

    D --> F["buildPointsToMapForFile(symbols, importedNames, maxIterations)"]
    F --> G["buildPointsToMap(..., maxIterations)"]
    G --> H["buildCallSiteTypeMap(pts, constraints, maxIterations)\nfor iter in 0..maxIterations"]

    E -->|NAPI u32| I["build_call_edges(files, nodes, builtins, max_iterations)"]

    C -->|Native-first pipeline| J["build_and_insert_call_edges(..., max_iterations)"]
    J --> I

    I --> K["process_file(..., max_iterations)"]
    K --> L["build_file_context(..., max_iterations)"]
    L --> M["build_pts_map_for_file(..., max_iterations)"]
    M --> N["build_points_to_map(..., max_iterations)\nfor _ in 0..max_iterations"]

    style H fill:#d4edda
    style N fill:#d4edda
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[".codegraphrc.json\nanalysis.pointsToMaxIterations"] -->|loadConfig TS| B["ctx.config.analysis\n.pointsToMaxIterations"]
    A -->|serde JSON| C["BuildConfig.analysis\n.points_to_max_iterations"]

    B -->|JS path| D["buildCallEdgesJS()"]
    B -->|Native JS-orchestrated| E["buildCallEdgesNative()\nnative.buildCallEdges(..., maxIterations)"]

    D --> F["buildPointsToMapForFile(symbols, importedNames, maxIterations)"]
    F --> G["buildPointsToMap(..., maxIterations)"]
    G --> H["buildCallSiteTypeMap(pts, constraints, maxIterations)\nfor iter in 0..maxIterations"]

    E -->|NAPI u32| I["build_call_edges(files, nodes, builtins, max_iterations)"]

    C -->|Native-first pipeline| J["build_and_insert_call_edges(..., max_iterations)"]
    J --> I

    I --> K["process_file(..., max_iterations)"]
    K --> L["build_file_context(..., max_iterations)"]
    L --> M["build_pts_map_for_file(..., max_iterations)"]
    M --> N["build_points_to_map(..., max_iterations)\nfor _ in 0..max_iterations"]

    style H fill:#d4edda
    style N fill:#d4edda
Loading

Comments Outside Diff (1)

  1. src/domain/graph/resolver/points-to.ts, line 549-551 (link)

    P2 Zero/negative maxIterations silently disables the solver

    for (let iter = 0; iter < maxIterations; iter++) runs zero iterations when maxIterations is 0, meaning no alias chains are ever propagated and the call graph is silently degraded. Now that this value is user-configurable via .codegraphrc.json, a value of 0 (or anything below 1) will suppress all points-to resolution without any warning. Adding a guard — either a Math.max(1, maxIterations) clamp here, or a validation step in loadConfig — would prevent a hard-to-debug misconfiguration. The same concern applies to the Rust side's for _ in 0..max_iterations loop in build_edges.rs.

    Fix in Claude Code

Fix All in Claude Code

Reviews (1): Last reviewed commit: "feat: wire points-to solver max-iteratio..." | Re-trigger Greptile

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Codegraph Impact Analysis

7 functions changed6 callers affected across 2 files

  • buildCallEdgesNative in src/domain/graph/builder/stages/build-edges.ts:623 (3 transitive callers)
  • buildCallEdgesJS in src/domain/graph/builder/stages/build-edges.ts:884 (3 transitive callers)
  • buildPointsToMapForFile in src/domain/graph/builder/stages/build-edges.ts:1074 (3 transitive callers)
  • buildCallSiteTypeMap in src/domain/graph/resolver/points-to.ts:365 (3 transitive callers)
  • buildPointsToMap in src/domain/graph/resolver/points-to.ts:414 (3 transitive callers)
  • CodegraphConfig.analysis in src/types.ts:1479 (0 transitive callers)
  • NativeAddon.buildCallEdges in src/types.ts:2282 (0 transitive callers)

@carlos-alm carlos-alm deleted the branch fix/issue-1752-blast-radius-incremental-stale July 6, 2026 19:27
@carlos-alm carlos-alm closed this Jul 6, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 6, 2026
@carlos-alm

Copy link
Copy Markdown
Contributor Author

Recreated as #1918 — this PR was inadvertently closed when its stacked base branch was deleted after #1916 merged. Continuing there.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant