Skip to content

fix(graph): bind builder method callers through typed locals (#1355) - #1361

Closed
ScriptedAlchemy wants to merge 6 commits into
codex/tracedecay-total-redesign-plan-reopenedfrom
agent/1355-false-complete-callers
Closed

ScriptedAlchemy wants to merge 6 commits into
codex/tracedecay-total-redesign-plan-reopenedfrom
agent/1355-false-complete-callers

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 16, 2026 •

Copy link
Copy Markdown
Owner

Summary

Fixes #1355: tracedecay_callers for WalkBuilder::build returned [] while the generation read as complete, even though other files call builder.build() through a typed local.

Measured failure class: extractor/resolver (class 1). Bare build is on the ubiquitous-name blocklist; dotted builder.build is dropped at retention; no WalkBuilder::build candidate reached seal.

Fix

  • Extractor tracks locals in lexical order (LocalTypeScope frames; record let after the initializer; push/pop on blocks/closures).
  • Unwrap mut / ref binding patterns before recording names.
  • Infer constructor types only from Type::new (or explicit type annotations) — not arbitrary associated calls like Factory::make().
  • Retention/seal keep qualified refs off the ubiquitous-name blocklist.
  • Seal binds Type::method / path::Type::method with full module-segment validation (crate path or same-crate module).

Codex P1 follow-up

Addressed review P1s on mut-only builders, shadowing/order, and Factory::make false edges; P2 module-segment resolve included.

Tests

  • Extraction: mut builder, shadow order, nested shadow, Factory::make abstention, typed annotation
  • cross_file_builder_method_calls_bind_through_typed_locals (mut-only fixtures)
  • MCP callers_of_cross_file_builder_method_include_typed_local_sites

Campaign base only (codex/tracedecay-total-redesign-plan-reopened / #707). Does not target master or #745.

Closes #1355.

Open in Web Open in Cursor 

Order 25b — #1361 binding redesign hold

HOLD MERGE. Four valid review findings require redesign (not four patches):

  1. mutable pattern — fix(graph): bind builder method callers through typed locals (#1355) #1361 (comment)
  2. lexical scope/order — fix(graph): bind builder method callers through typed locals (#1355) #1361 (comment)
  3. arbitrary associated-call result inference — fix(graph): bind builder method callers through typed locals (#1355) #1361 (comment)
  4. full qualified module segments — fix(graph): bind builder method callers through typed locals (#1355) #1361 (comment)

Owner e31ae27f: lexical scope stack + source-order walk; unwrap single-id patterns only; no constructor guessing; full module segments; tests that fail on old head. Subtract/close-as-unsafe if a large inference engine would be required.


Order 25 drain — lexical redesign on head


Closure decision (2026-09-16)

Closed unmerged as unsafe after two independent exact-head reviews. The +886-line local-type redesign still has five P1 semantic classes: unknown/control-flow bindings leak prior types; let … else alternatives are skipped; Type::new() is treated as fabricated return-type evidence; aliases/relative/inline module paths are not canonically resolved; and unanchored qualified matching can seal unrelated cross-crate methods. These defects can create convincing false graph edges, which is worse than abstaining. #1355 remains open and must be fixed through existing canonical signature/import/re-export/type authorities in a smaller PR. The branch and backup snapshot remain investigation evidence; do not merge piecemeal.

@changeset-bot

changeset-bot Bot commented Sep 16, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 1c1936c

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

Emit Type::method for receiver calls when the local's type is known,
keep qualified refs off the ubiquitous-name blocklist, and resolve
associated methods at seal so WalkBuilder::build callers are not empty.

Closes #1355

Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
@ScriptedAlchemy
ScriptedAlchemy force-pushed the agent/1355-false-complete-callers branch from 34573f0 to 1194a08 Compare September 16, 2026 05:48
@ScriptedAlchemy
ScriptedAlchemy marked this pull request as ready for review September 16, 2026 05:48
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-16T06:30:35.206593Z 1c1936c Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1194a08346

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/tracedecay-code-extraction/src/rust_extractor.rs Outdated
Comment thread crates/tracedecay-code-extraction/src/rust_extractor.rs Outdated
Comment thread crates/tracedecay-code-extraction/src/rust_extractor.rs Outdated
Comment thread crates/tracedecay-code-index/src/production/helpers.rs Outdated
@ScriptedAlchemy
ScriptedAlchemy marked this pull request as draft September 16, 2026 05:55
cursoragent and others added 5 commits September 16, 2026 06:03
Walk locals in lexical order with mut/ref unwrap, infer only from
Type::new or annotations, and resolve full module paths at seal.

Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Preserve source-ordered lexical binding from backup/1361-local-snapshot-59f5165102
on origin/agent/1355-false-complete-callers @ 000bf20 without force-push.
The 59f5165 snapshot still used function-wide withhold-on-rebind.
Restore tip's source-ordered LocalTypeScope, Type::new-only inference,
single-id mut/ref unwrap, and full module-segment seal from 000bf20.
@ScriptedAlchemy
ScriptedAlchemy marked this pull request as ready for review September 16, 2026 06:25

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1c1936c48d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1795 to +1799
let (type_path, ctor) = callee_name.rsplit_once("::")?;
if ctor != "new" || type_path.is_empty() {
return None;
}
Some(type_path.to_owned())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Verify new return types before qualifying locals

The fresh constructor_type_path implementation still assumes every Type::new() returns Type, but Rust gives new no special return-type semantics. For Factory::new() -> Product, let value = Factory::new(); value.build() emits Factory::build instead of Product::build; if Factory also has a build method, sealing records a false caller edge, and otherwise the blocklisted bare build leaves a complete-looking miss. Abstain unless the declared return type establishes Self/Type.

Useful? React with 👍 / 👎.

Comment on lines +1050 to +1052
let expected = format!("{type_name}::{method}");
if relative != expected && !relative.ends_with(&format!("::{expected}")) {
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve imported aliases before matching method owners

When a constructor uses an imported alias, such as use dep::WalkBuilder as Builder; let b = Builder::new(); b.build(), extraction emits Builder::build, but this comparison rejects the real WalkBuilder::build candidate before the alias's import binding is consulted below. Consequently this common aliased form still returns no cross-file caller; use the binding's imported_name when constructing the expected owner name.

Useful? React with 👍 / 👎.

Comment on lines +1121 to +1126
let module_after_crate = module_prefix[1..].join("/");
return if module_after_crate.is_empty() {
target_module.is_empty()
} else {
target_module == module_after_crate
|| target_module.starts_with(&format!("{module_after_crate}/"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match inline modules by the symbol path

For a valid external path like dep::a::Builder::new() where a is an inline module in dep/src/lib.rs, the target symbol is a::Builder::build but target_module is empty because it is derived only from the file path. The nonempty module_after_crate therefore rejects the candidate, so calls to builder types defined in inline modules never bind. Account for inline module segments from the target symbol's qualified path rather than requiring them to appear in the filename.

Useful? React with 👍 / 👎.

@github-actions

Copy link
Copy Markdown
Contributor

Performance Comparison codex/tracedecay-total-redesign-plan-reopened → agent/1355-false-complete-callers

Total Elapsed Time: 8.06s → 7.91s (-1.9%)
CPU Baseline: 84.36µs → 84.34µs (-0.0%)
Benchmark ID: index-bench-timing

timing - Execution duration of functions.

+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+
| Function                                 | Calls                      | Avg                             | P95                              | Total                          | % Total                      |
+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+
| tracedecay-index-bench                   | 1 → 1 (+0.0%)              | 8.06s → 7.91s (-1.9%)           | 8.07s → 7.91s (-2.0%)            | 8.06s → 7.91s (-1.9%)          | 100.00% → 100.00% (+0.0%)    |
+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+
| domain.canonical.sha256                  | 85787 → 85787 (+0.0%)      | 34.55µs → 34.17µs (-1.1%)       | 108.80µs → 109.12µs (+0.3%)      | 2.96s → 2.93s (-1.0%)          | 36.76% → 37.06% (+0.8%)      |
+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+
| code_index.workers.install               | 81 → 81 (+0.0%)            | 32.89ms → 34.00ms (+3.4%)       | 200.93ms → 205.52ms (+2.3%)      | 2.66s → 2.75s (+3.4%)          | 33.04% → 34.82% (+5.4%)      |
+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+
| query.artifact.finalization.advance_wake | 28 → 28 (+0.0%)            | 55.17ms → 51.98ms (-5.8%)       | 421.00ms → 407.63ms (-3.2%)      | 1.54s → 1.46s (-5.2%)          | 19.16% → 18.40% (-4.0%)      |
+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+
| code_index.build.and_publish             | 2 → 2 (+0.0%)              | 748.53ms → 720.17ms (-3.8%)     | 1.05s → 1.02s (-2.9%)            | 1.50s → 1.44s (-4.0%)          | 18.57% → 18.21% (-1.9%)      |
+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+
| query.artifact.batch.sqlite              | 5 → 5 (+0.0%)              | 292.24ms → 284.67ms (-2.6%)     | 320.60ms → 322.44ms (+0.6%)      | 1.46s → 1.42s (-2.7%)          | 18.12% → 17.99% (-0.7%)      |
+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+
| code_index.extract.parser_artifact       | 276 → 276 (+0.0%)          | 4.22ms → 4.09ms (-3.1%)         | 7.02ms → 6.68ms (-4.8%)          | 1.16s → 1.13s (-2.6%)          | 14.43% → 14.27% (-1.1%)      |
+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+
| query.artifact.batch.postings            | 5 → 5 (+0.0%)              | 227.37ms → 222.67ms (-2.1%)     | 254.80ms → 257.16ms (+0.9%)      | 1.14s → 1.11s (-2.6%)          | 14.10% → 14.08% (-0.1%)      |
+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+
| query.artifact.batch.parallel_prepare    | 5 → 5 (+0.0%)              | 191.46ms → 218.61ms (+14.2%)    | 212.73ms → 267.39ms (+25.7%) ⚠️  | 957.32ms → 1.09s (+13.9%)      | 11.87% → 13.82% (+16.4%)     |
+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+
| domain.canonical.json_bytes              | 206681 → 206681 (+0.0%)    | 4.28µs → 4.98µs (+16.4%)        | 4.49µs → 4.48µs (-0.2%)          | 883.80ms → 1.03s (+16.5%)      | 10.96% → 13.01% (+18.7%)     |
+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+
| code_index.restore.file_admit            | 260 → 260 (+0.0%)          | 2.88ms → 2.84ms (-1.4%)         | 4.63ms → 4.58ms (-1.1%)          | 749.39ms → 739.15ms (-1.4%)    | 9.29% → 9.34% (+0.5%)        |
+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+
| code_index.build.materialize_full        | 1 → 1 (+0.0%)              | 757.81ms → 736.55ms (-2.8%)     | 758.12ms → 736.62ms (-2.8%)      | 757.81ms → 736.55ms (-2.8%)    | 9.40% → 9.31% (-1.0%)        |
+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+
| code_index.collect.materialize_full      | 1 → 1 (+0.0%)              | 742.18ms → 722.25ms (-2.7%)     | 742.39ms → 722.47ms (-2.7%)      | 742.18ms → 722.25ms (-2.7%)    | 9.20% → 9.13% (-0.8%)        |
+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+
| query.artifact.batch.postings.ngram_rows | 5 → 5 (+0.0%)              | 129.24ms → 126.02ms (-2.5%)     | 144.83ms → 146.54ms (+1.2%)      | 646.22ms → 630.11ms (-2.5%)    | 8.01% → 7.97% (-0.5%)        |
+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+
| code_index.build.assemble                | 2 → 2 (+0.0%)              | 297.89ms → 282.97ms (-5.0%)     | 304.61ms → 290.72ms (-4.6%)      | 595.79ms → 565.93ms (-5.0%)    | 7.39% → 7.15% (-3.2%)        |
+------------------------------------------+----------------------------+---------------------------------+----------------------------------+--------------------------------+------------------------------+

Generated with hotpath-rs

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