Skip to content

Resync with graphify-py @ 544f95e (v0.9.2)#33

Merged
rblaine95 merged 7 commits into
masterfrom
resync/graphify-py/544f95e
Jun 30, 2026
Merged

Resync with graphify-py @ 544f95e (v0.9.2)#33
rblaine95 merged 7 commits into
masterfrom
resync/graphify-py/544f95e

Conversation

@rblaine95

@rblaine95 rblaine95 commented Jun 30, 2026

Copy link
Copy Markdown
Member

Ports graphify-py v0.9.0v0.9.2 (commit 544f95e) into the Rust workspace, advancing the submodule pointer and bumping the workspace version in lockstep. The submodule update is its own commit (0d4f019); the Rust ports follow.

Ported

graphify-extract

  • tsconfig multi-target alias fallback (#1531): keep all declared paths targets and resolve each on disk in order.
  • package.json exports subpath map (#1308): resolve exports conditions (priority source > import > module > svelte > types > require > default) with a symlink-safe package-containment guard.
  • alias/workspace import remap, absolute form (#1529): register both the input-form and absolute-resolved prefix so resolver-minted symbol edges reconnect.
  • Java type-parameter skip + record components (#1518/#1519): scope-aware type-parameter filtering; emit record-component type references.
  • Ruby type-aware member-call resolution (#1499): infer var = ClassName.new bindings and resolve var.method() by receiver type.
  • Swift type-qualified confidence (#1533): Type.staticMethod() is EXTRACTED (1.0); instance calls inferred via local type are INFERRED (0.8).
  • ObjC fixes (#1475): blank NS_ASSUME_NONNULL macros before parsing; resolve quoted #import to real files; emit alloc/init references.
  • node-id collision salt + ObjC header remap (#1522/#1475): append a sha1 of the source path when a separator-vs-punctuation collision survives; repoint quoted-include edges to the header variant.
  • origin_file handling (#1515/#1516): drop origin_file from the final output (internal disambiguation hint); set it on Go cross-file stubs so same-label stubs split per file.

Other crates

  • graphify-cache: prune_semantic_cache + CLI wiring (#1527) — sweep orphaned semantic-cache entries after extraction.
  • graphify-export: GraphML null-attribute coercion parity test (#1502).
  • graphify-llm: GRAPHIFY_MAX_RETRIES (default 6) + a 429/transient retry loop on ureq sends with exponential backoff (#1523/#1442).
  • graphify-watch: prune edges owned by a re-extracted file (#1521).
  • graphify-hooks: skill install-string update (#1530).
  • CLI: save-result --answer-file (#1502).

Docs: workspace version → 0.9.2 (EXTRACTOR_VERSION bumped, invalidating the old AST cache); GRAPHIFY_MAX_RETRIES, GRAPHIFY_API_TIMEOUT, GRAPHIFY_MAX_OUTPUT_TOKENS, and --answer-file documented in USAGE.md.

Verification

  • cargo nextest run --workspace: 2333 passed (2 feature-gated skips).
  • cargo clippy --all-targets --all-features --workspace: clean.
  • hk check: clean.
  • New parity tests ported for every new graphify-py/tests/test_*.py case.

CodeRabbit

Three review rounds. Round 1's 7 actionable findings fixed; round 2 surfaced three latent bugs in the colliding-node-id pass (header remap mis-targeting non-C imports, ambiguous header selection, and a salt that could collide outside its group) — these matched the graphify-py reference, but per AGENTS.md a bug in the reference is not a requirement, so they're fixed in Rust with the divergence documented in code comments and covered by regression tests. Final review: 0 findings.

Divergences from graphify-py

  • The colliding-node-id pass hardens three corner cases the reference handles loosely (see crates/graphify-extract/src/postprocess.rs comments).
  • GRAPHIFY_RETRY_BASE_MS is a Rust-only test seam for the retry backoff base.

Coverage note

The ≥95%-per-touched-crate target is a pre-existing baseline gap (the workspace sits ~85%). New code is well-covered (resolver framework 100%, Ruby resolver ~95%, GraphML null path 100%); the shortfall is untouched pre-existing low-coverage files, not a regression from this resync.

Summary by CodeRabbit

  • New Features
    • save-result now supports --answer-file, with --answer-file taking priority over --answer.
    • Added LLM HTTP rate-limit retry controls via environment variables (GRAPHIFY_MAX_RETRIES, GRAPHIFY_RETRY_BASE_MS, GRAPHIFY_API_TIMEOUT).
  • Bug Fixes
    • Automatic pruning of orphan semantic cache entries after semantic extraction.
    • Rebuild now prunes removed import edges to avoid stale results.
    • Export robustness: GraphML export now tolerates null attribute values.
  • Chores
    • Documentation updates, version bump to 0.9.2, and expanded parity/integration test coverage.

Port the v0.9.0..v0.9.2 changes from the `graphify-py` reference into
the Rust workspace.

Extraction (`graphify-extract`):
- tsconfig `paths` keep all targets, trying each on disk (#1531)
- package.json `exports` subpath map with condition priority, a
  single `./*` wildcard, and a path-escape guard (#1308)
- alias/workspace import targets remap via the absolute-resolved id
  form so relative-input runs do not orphan symbol edges (#1529)
- skip Java type-parameter references; emit record-component
  references (#1518, #1519)
- type-aware Ruby member-call resolution: `Class.new` and typed
  `var.method` resolve by receiver type, dispatched through a small
  resolver registry (#1499)
- Swift type-qualified static calls are EXTRACTED, locally-inferred
  instance calls stay INFERRED (#1533)
- ObjC: blank `NS_ASSUME_NONNULL` macros, resolve a quoted `#import`
  to the real header, emit `[[Foo alloc] init]` type references (#1475)
- salt separator-colliding node ids with a path hash; repoint ObjC
  header imports to the disambiguated `.h` node (#1522, #1475)
- record `origin_file` on the Go reference stub; drop `origin_file`
  from extract output so no absolute path leaks (#1515, #1516)

Other crates:
- `prune_semantic_cache` sweeps orphaned content-hash entries, wired
  into the extract command (#1527)
- GraphML export pins null-attribute coercion to the empty string
  (#1502)
- `GRAPHIFY_MAX_RETRIES` plus a 429 retry loop around the LLM HTTP
  sends; the secondary dispatch path keeps its timeout (#1523, #1442)
- `graphify update` prunes edges owned by a re-extracted file (#1521)
- skill and AGENTS install strings drop the host-specific `skill`
  tool reference (#1530)
- `save-result --answer-file` reads the answer from a file (#1502)

Bump the workspace version to 0.9.2 in lockstep with the submodule
pointer and document `--answer-file` and the retry env vars in
`USAGE.md`.

Glory to the Omnissiah
Apply the actionable findings from the first CodeRabbit pass.

- `contained_in_package` canonicalises the nearest existing ancestor of
  the candidate so a symlink that crosses the package boundary is
  rejected, not just a lexical `..` escape (closer to Python's
  `Path.resolve()`).
- correct the `send_json_with_retry` doc: only HTTP 429 is retried, 5xx
  fails fast.
- assert the exact default retry count (6) instead of a lower bound.
- mark the env-mutating 429 retry tests `#[serial(env)]`.
- route aliased imports through `resolve_js_module_path` in the Svelte
  and JS import branches so a `.js` specifier backed by a `.ts` source
  hashes to the same node id; add a regression test.
- select the file node by label in the non-salted id test so it is
  order-independent.
- `USAGE.md`: `--answer-file` takes precedence when both answer inputs
  are given; at least one is required.

By the will of the Machine God
CodeRabbit flagged three latent bugs in the colliding-node-id pass. The
`graphify-py` reference has the same gaps, but per AGENTS.md a bug in the
reference is not a requirement, and each violates an observable contract
in a collision corner case. The divergences are documented in code.

- restrict the header-variant repoint to C-family importers. A non-C
  `imports_from` whose target merely collides with a C header id was
  silently rewritten to the header, mis-targeting the edge.
- only repoint to a header when the colliding group holds exactly one
  header candidate; the reference picks the first in node order, which is
  arbitrary when two same-stem headers (`foo.h`/`foo.hpp`) collide.
- guard a salted id against colliding with an id surviving outside its
  group, widening the salt until globally unique. `occupied` holds the
  surviving ids (non-ambiguous, plus ambiguous groups with an empty-key
  member that keeps the bare id), so the guard never over-hashes against
  an id that is itself about to be rewritten.

Extract `rewrite_edge_endpoints` to keep the pass under the line limit;
add regression tests for the non-C import and the cross-group salt.

By the will of the Machine God
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 579f5ee2-482a-485c-bd7d-9eba158cc818

📥 Commits

Reviewing files that changed from the base of the PR and between f451a97 and ec1c325.

📒 Files selected for processing (2)
  • crates/graphify-extract/src/extractors/multi/swift.rs
  • crates/graphify-extract/tests/swift_member_calls.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/graphify-extract/src/extractors/multi/swift.rs

📝 Walkthrough

Walkthrough

This PR bumps the workspace to version 0.9.2 and adds Ruby type-aware cross-file call resolution, tsconfig alias and workspace export resolution updates, node-ID collision handling changes, ObjC and Java extractor changes, LLM retry handling, semantic cache pruning, watch edge eviction, and a --answer-file option for save-result.

Changes

Extraction pipeline enhancements

Layer / File(s) Summary
RawCall receiver_type propagation
crates/graphify-extract/src/types.rs, crates/graphify-extract/src/extractors/multi/cache.rs, crates/graphify-extract/src/extractors/dm/source.rs, crates/graphify-extract/src/extractors/elixir.rs, crates/graphify-extract/src/extractors/go/calls.rs, crates/graphify-extract/src/extractors/powershell/mod.rs, crates/graphify-extract/src/extractors/rust_lang/calls.rs, crates/graphify-extract/src/extractors/zig.rs
Adds receiver_type: Option<String> to RawCall, stores it in the AST cache, and initializes it to None across non-Ruby extractor call sites.
Ruby local type inference
crates/graphify-extract/src/lang_configs.rs, crates/graphify-extract/src/generic/ruby.rs, crates/graphify-extract/src/generic/mod.rs, crates/graphify-extract/src/generic/calls.rs
Corrects the Ruby language ID, implements ruby_local_class_bindings, threads Ruby receiver-type context through call walking, and captures Ruby callees with member-call shape.
Ruby cross-file resolver dispatch
crates/graphify-extract/src/extractors/multi/resolvers.rs, crates/graphify-extract/src/extractors/multi/ruby.rs, crates/graphify-extract/src/extractors/multi/mod.rs, crates/graphify-extract/tests/ruby_resolution.rs, crates/graphify-extract/tests/swift_member_calls.rs
Introduces the resolver registry, implements Ruby cross-file member-call resolution, replaces hardcoded Swift/Python resolver wiring with suffix-driven dispatch, and adds Swift confidence tiers and Ruby resolution tests.
tsconfig multi-target aliases
crates/graphify-extract/src/tsconfig.rs, crates/graphify-extract/src/extractors/svelte.rs, crates/graphify-extract/src/generic/js_extra.rs, crates/graphify-extract/tests/js_alias_relative_paths.rs, crates/graphify-extract/tests/js_workspace_resolution.rs, crates/graphify-extract/tests/parity.rs
Changes tsconfig alias storage to preserve multiple candidate bases, adds resolve_tsconfig_alias, and refactors Svelte and JS import resolution to use it.
Workspace exports subpath resolution
crates/graphify-extract/src/workspace.rs, crates/graphify-extract/tests/js_workspace_resolution.rs
Adds conditional and wildcard exports resolution, package containment checks, and updates JS workspace resolution tests.
Node-ID collision salting
crates/graphify-extract/Cargo.toml, crates/graphify-extract/src/postprocess.rs, crates/graphify-extract/src/extractors/multi/mod.rs, crates/graphify-extract/src/extractors/go/refs.rs, crates/graphify-extract/tests/id_collision.rs, crates/graphify-extract/tests/cross_file_multi.rs, crates/graphify-extract/tests/parity_postprocess.rs
Adds SHA-1-based collision salting, header remapping, multi-prefix remap handling, and supporting tests and stub-source adjustments.
ObjC preprocessing and include resolution
crates/graphify-extract/src/import_handlers.rs, crates/graphify-extract/src/extractors/objc.rs, crates/graphify-extract/tests/objc_resolution.rs
Blanks NS_ASSUME_NONNULL_* macros before parsing, centralizes ObjC sourceless node creation, resolves quoted includes through C include path lookup, and updates alloc call handling and related tests.
Java reference filtering and record components
crates/graphify-extract/src/generic/references/java.rs, crates/graphify-extract/src/generic/walk.rs, crates/graphify-extract/tests/java_type_resolution.rs, crates/graphify-extract/tests/parity_languages.rs, crates/graphify-export/tests/parity.rs
Filters Java type-parameter references, emits record-header component references, and adds Java parity coverage plus a GraphML null-attribute test.

LLM retry

Layer / File(s) Summary
Retry helper and backend wiring
crates/graphify-llm/src/openai_compat.rs, crates/graphify-llm/src/azure.rs, crates/graphify-llm/src/call.rs, crates/graphify-llm/src/claude.rs, crates/graphify-llm/src/kimi.rs
Adds resolve_max_retries and send_json_with_retry, and routes backend HTTP requests through the retry helper.
Retry tests and docs
crates/graphify-llm/tests/openai_compat_helpers.rs, crates/graphify-llm/tests/per_backend_http.rs, USAGE.md
Validates retry and disable behavior and documents the new environment variables.

Cache, watch, CLI, hooks, and versioning

Layer / File(s) Summary
Semantic cache pruning
crates/graphify-cache/src/semantic.rs, crates/graphify-cache/src/lib.rs, crates/graphify-cache/tests/parity.rs, src/cli/extract.rs
Adds semantic cache pruning, re-exports the helper, wires pruning into semantic extraction, and covers prune behaviors in tests.
Watch rebuild edge eviction
crates/graphify-watch/src/rebuild/pipeline_helpers.rs, crates/graphify-watch/tests/rebuild_pipeline.rs
Adds edge-level eviction in graph merge and validates removal of stale import edges after rebuild.
CLI save-result --answer-file option
src/cli/args.rs, src/cli/dispatch.rs, USAGE.md, tests/cli_commands.rs
Makes --answer optional, adds --answer-file, resolves file-vs-inline precedence, documents the command, and covers both CLI paths in tests.
Hooks text, version, and submodule
crates/graphify-hooks/src/platform/common/markdown.rs, crates/graphify-hooks/src/platform/common/skills.rs, crates/graphify-hooks/tests/parity.rs, Cargo.toml, graphify-py
Updates /graphify trigger wording in shared hook text, adds hook parity tests, bumps the workspace version, and advances the graphify-py submodule pointer.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐇 I hopped through hashes, aliases, and stars,
While retrying 429s from distant LLM bars.
Ruby found its types, and old caches grew thin,
With answer files and new edges neatly tucked in.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: resyncing the Rust workspace with graphify-py at a specific commit and version bump.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

coderabbitai[bot]

This comment was marked as resolved.

Resolve the second review round; divergences from `graphify-py` are
documented in code comments (where the reviewer reads them).

Resolution accuracy:
- Ruby: index class-definition nodes from `contains` (not only `method`
  edges) so a method-less class still resolves `Class.new`; defer Ruby
  member calls before the same-file bare-name lookup so a typed `p.run`
  resolves by receiver type instead of being swallowed by a local `run`;
  skip nested `class`/`module` scopes in the local-binding walk.
- Swift: a receiver bound as a local resolves via its inferred type and
  is INFERRED even when upper-cased, so an upper-cased local is no longer
  mis-marked as a type-qualified EXTRACTED call.

Id disambiguation and resolution:
- postprocess: track ids minted within a pass so a later collision group
  can't reuse an earlier group's salted id, while still collapsing
  same-file same-id nodes onto one remap entry.
- tsconfig: match the longest alias prefix so `@/feature/*` wins over a
  broad `@/*`.
- workspace: resolve a bare conditional-`exports` object (no `.` key) as
  the package entry, and reject `exports` targets that escape the
  package directory on the no-subpath path too.

CLI / cache:
- `--answer-file` content is preserved exactly (no trim), matching
  inline `--answer`.
- the semantic-cache prune resolves relative paths against `root`, as
  the cache lookup does, so a valid entry isn't pruned on relative input.

Tests and docs:
- cover the negative `GRAPHIFY_MAX_RETRIES` fallback and assert the
  no-retry mock is hit exactly once; assert the full save-result
  requirement message; document `GRAPHIFY_RETRY_BASE_MS`.
- regression tests for the method-less Ruby class and the cross-group
  salt collision; `#[must_use]` on `default_resolvers`; new tests use
  `?`-returning signatures.

By the will of the Machine God
CodeRabbit asked the test to assert the non-C import resolves to the
Python variant, but the salt remap is keyed by the importer's own file
and cannot infer the target module for a bare ambiguous import when the
importer differs from the definition (the graphify-py reference doesn't
either). Document that in the test and assert the gate the fix actually
guarantees -- a non-C import is never repointed at the header variant --
rather than pinning a dangling id.

By the will of the Machine God
coderabbitai[bot]

This comment was marked as resolved.

The file-wide local type table is not scope-aware, so consulting it
before the receiver's syntax could demote a genuine
`Type.staticMethod()` to an INFERRED call on whatever type a same-named
local was bound to elsewhere in the file. Resolve an upper-cased
receiver as the named type (EXTRACTED) and consult the table only for
lower-cased receivers, which re-aligns with graphify-py
(extract.py:9553) and removes the divergence. Add a regression test for
the pollution case (an upper-cased parameter binding alongside a real
type-qualified call).

The companion review note about `member_call_captures_receiver` missing
`#[test]` is a false positive: the attribute is present and the test
runs (confirmed via `nextest -E 'test(member_call_captures_receiver)'`).

By the will of the Machine God
@rblaine95 rblaine95 force-pushed the resync/graphify-py/544f95e branch from 5620bfc to ec1c325 Compare June 30, 2026 09:43
@rblaine95 rblaine95 merged commit b42b4d4 into master Jun 30, 2026
8 of 11 checks passed
@rblaine95 rblaine95 deleted the resync/graphify-py/544f95e branch June 30, 2026 09:53
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.

1 participant