Skip to content

Resync with graphify-py @ 92e682f (v0.9.0)#31

Merged
rblaine95 merged 8 commits into
masterfrom
resync/graphify-py/92e682f
Jun 29, 2026
Merged

Resync with graphify-py @ 92e682f (v0.9.0)#31
rblaine95 merged 8 commits into
masterfrom
resync/graphify-py/92e682f

Conversation

@rblaine95

@rblaine95 rblaine95 commented Jun 29, 2026

Copy link
Copy Markdown
Member

Ports the applicable changes from graphify-py v0.8.49 → v0.9.0 (38 upstream commits) and bumps the workspace to 0.9.0. Submodule pointer advanced to 92e682f.

Breaking change

  • Node IDs are now the full repo-relative path (extension dropped, separators → _), e.g. src/auth/session.pysrc_auth_session, validate() inside → src_auth_session_validate (#1504/#1509). Same-named files in different directories no longer collide. A build_from_json re-key migrates pre-#1504 graphs; read-only consumers (query/serve) print a one-line rebuild nudge instead of silently re-keying.

Ported

  • Sourceless cross-file type-ref stubs carry an origin_file so same-label stubs from different files stay distinct (#1462/#1500).
  • C# cross-file type resolver + enum/struct/record node types (#1466).
  • Java enum/annotation node types, generic-arg + field-type references (#1512/#1510/#1485/#1487).
  • Objective-C header routing, generic property types, class-method selectors, @import (#1475).
  • New WPF/XAML extractor: x:Class, named controls, {Binding} paths, ViewModel / code-behind resolution (#1460/#1473).
  • Vue SFC extraction + .metal routed through the C++ extractor (#1468/#1480).
  • Export: Obsidian manifest-guarded writes, Canvas ceil(sqrt(n)) grid, case-fold filename dedup (#1506/#1452/#1453); wiki portable relative-Markdown links (#1444).
  • LLM: requests send stream: false; kimi/gemini/deepseek honour a bare *_BASE_URL (#1223/#1458).
  • CLI: --timing on extract/cluster-only/label; --missing-only on cluster-only/label (#1490/#1481).
  • reflect/hooks/affected/explain: --if-stale weighs graph sidecars; the Read|Glob hook matches the real trailing extension; affected/explain resolve a source-file path to its file-level node (#1470/#1463/#1503).

Docs (README.md, USAGE.md) reconciled: new flags, formats, env vars, node-ID scheme, and export behaviours. Parity tests ported for every new upstream test case.

Verification

  • cargo clippy --all-targets --all-features --workspace — clean
  • cargo nextest run --workspace — 2283 passed, 2 skipped
  • hk check — pass
  • CodeRabbit --type committedfindings: 0 (24 first-pass findings resolved: genuine divergences fixed, graphify-py-matching ones kept with in-code // Parity dispute (CodeRabbit): … comments)

Summary by CodeRabbit

  • Chores
    • Bumped the workspace version to 0.9.0 (and expanded dev test dependencies).
  • Documentation
    • Updated CLI docs and README features for Vue/Svelte/Astro/Ast xaml handling, node ID rules, and new CLI flags & env vars.
  • New Features
    • Added/expanded Vue, Svelte, Astro, and XAML extraction; recognized .metal as code.
    • Added optional --timing and --missing-only; expanded backend base-URL env overrides (KIMI/GEMINI/DEEPSEEK).
  • Bug Fixes
    • Improved trailing-slash handling, file-node selection, and node-ID stability for path collisions; enhanced export determinism and safety.
  • Tests
    • Added/updated parity and integration tests for affected/path resolution and export/linking behavior.

Port the applicable feature, security, and bug-fix changes from
`graphify-py` v0.8.49 -> v0.9.0 (38 upstream commits) and bump the
workspace to `0.9.0`.

Ports:

- Node IDs are now the full repo-relative path with the extension
  dropped and separators collapsed to `_`, so same-named files in
  different directories stay distinct (#1504, #1509). Legacy graphs
  read as-is with a one-line rebuild nudge.
- Cross-file type-reference stubs are sourceless and carry an
  `origin_file` so same-label stubs from different files do not merge
  (#1462, #1500).
- New C# cross-file type resolver plus `enum`/`struct`/`record` node
  types (#1466).
- Java `enum`/annotation node types, generic-argument and field-type
  references (#1512, #1510, #1485, #1487).
- Objective-C header routing, generic property types, class-method
  selectors, and `@import` handling (#1475).
- New WPF/XAML extractor: `x:Class`, named controls, `{Binding}`
  paths, and ViewModel / code-behind resolution (#1460, #1473).
- Vue single-file-component extraction and `.metal` routing through
  the C++ extractor (#1468, #1480).
- Obsidian export only overwrites notes it owns via a manifest, the
  Canvas uses a `ceil(sqrt(n))` grid, and note/canvas filenames are
  de-duplicated case-insensitively (#1506, #1452, #1453).
- Wiki articles link with portable relative Markdown links instead of
  `[[wikilinks]]`, with case-folded slug dedup (#1444, #1453).
- LLM requests send `stream: false`, and `kimi`/`gemini`/`deepseek`
  honour a bare `*_BASE_URL` (#1223, #1458).
- CLI `--timing` on `extract`/`cluster-only`/`label`, and
  `--missing-only` on `cluster-only`/`label` (#1490, #1481).
- `reflect --if-stale` also weighs the graph sidecars, the Read|Glob
  hook matches a file's real trailing extension, and `affected` /
  `explain` resolve a source-file path to its file-level node
  (#1470, #1463, #1503).

Glory to the Omnissiah
Resolve the 24 findings from the first CodeRabbit pass on the resync.
Genuine divergences from graphify-py are fixed; findings that match
graphify-py are kept and the rationale recorded in an in-code comment
(CodeRabbit reads code, not commit messages).

Fixes:

- `extract()` now clears the thread-local XAML `ViewModel` class cache
  per run (generation-gated across the rayon pool), matching graphify-py
  `_XAML_CSHARP_CLASS_CACHE.clear()`; a repeated in-process run re-scans
  `.cs` instead of serving stale members. Adds a regression test.
- Obsidian manifest write propagates IO errors instead of swallowing
  them, like the sibling note writes and graphify-py `.write_text`.
- `ingest` C-family map includes `.cxx` (extracted as C++ and present in
  `analyze`'s family map; graphify-py's build map omits it).
- Legacy-id alias registration is skipped unless the node id shares the
  new stem, so a mismatched prefix no longer maps unrelated edges.
- `collect_cs_files` uses `entry.file_type()` so a symlinked directory
  cannot loop forever.
- `is_objc_header` reads only the 256 KiB it inspects.
- Vue dynamic-import recovery scans the masked source, so an `import()`
  in `<template>`/`<style>` is not edged.

Kept as graphify-py parity (documented in code):

- Sourceless stub `source_location` stays `Some("")` (graphify-py emits
  `""`); wiki resolver keys by label and reserves `index` only in the
  resolver; C# merges a file's namespaces into one scope; the direct
  `<Binding Path>` element emits no generated-member edge; validation
  runs once before the semantic re-key.

Also strengthens the affected test assertions (exact set/count checks,
`origin_file` and canonical-id checks, real absolute/relative paths, raw
event-edge count, loud directory-walk errors).

By the will of the Machine God
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

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: ed96f8c4-eb6a-44f4-a247-2ea1cdf6a459

📥 Commits

Reviewing files that changed from the base of the PR and between a690728 and 6bf2f0d.

📒 Files selected for processing (7)
  • Cargo.toml
  • crates/graphify-extract/src/extractors/objc.rs
  • crates/graphify-serve/src/graph.rs
  • crates/graphify-serve/tests/parity.rs
  • crates/graphify-wiki/src/generate.rs
  • src/cli/cluster_only.rs
  • tests/cli_commands.rs
🚧 Files skipped from review as they are similar to previous changes (7)
  • crates/graphify-serve/tests/parity.rs
  • Cargo.toml
  • tests/cli_commands.rs
  • crates/graphify-serve/src/graph.rs
  • crates/graphify-wiki/src/generate.rs
  • crates/graphify-extract/src/extractors/objc.rs
  • src/cli/cluster_only.rs

📝 Walkthrough

Walkthrough

This release bumps the workspace to 0.9.0, changes node IDs to full repo-relative stems, adds origin_file to nodes, introduces migration helpers and new extractors, updates export/link behavior, and extends CLI, LLM, hook, and docs handling.

Changes

Graphify 0.9.0 release

Layer / File(s) Summary
Node IDs and migration
crates/graphify-extract/src/types.rs, crates/graphify-extract/src/ids.rs, crates/graphify-extract/src/ids_tests.rs, crates/graphify-extract/src/postprocess.rs, crates/graphify-extract/src/postprocess_tests.rs, crates/graphify-extract/src/symbol_resolution_tests.rs, crates/graphify-extract/src/lib.rs, crates/graphify-extract/src/lang_configs.rs, crates/graphify-extract/tests/file_node_id_spec.rs, crates/graphify-build/src/migrate.rs, crates/graphify-build/src/lib.rs, crates/graphify-build/src/build_fn.rs, crates/graphify-build/src/ingest.rs, crates/graphify-build/tests/parity.rs, crates/graphify-serve/src/graph.rs, crates/graphify-serve/tests/parity.rs, crates/graphify-affected/src/lib.rs, crates/graphify-affected/tests/parity.rs, tests/cli.rs, src/cli/mod.rs
file_stem now returns the full repo-relative path stem, Node gains origin_file, migration helpers re-key and alias legacy IDs, and source-file queries prefer file-level nodes.
Origin-file propagation and generic stubs
crates/graphify-extract/src/extractors/*, crates/graphify-extract/src/generic/*, crates/graphify-extract/tests/parity_postprocess.rs, crates/graphify-extract/tests/cross_file_multi.rs
origin_file: None is added across extractor node construction, and generic named-node helpers now create file-anchored stubs with updated call sites and tests.
XAML, Vue, ObjC, and Metal extraction
crates/graphify-extract/src/extractors/dotnet/xaml.rs, crates/graphify-extract/src/extractors/dotnet/mod.rs, crates/graphify-extract/src/extractors/multi/csharp.rs, crates/graphify-extract/src/extractors/multi/mod.rs, crates/graphify-extract/src/extractors/multi/cache.rs, crates/graphify-extract/src/extractors/svelte.rs, crates/graphify-extract/src/extractors/objc.rs, crates/graphify-detect/src/extensions.rs, crates/graphify-analyze/src/classify.rs, crates/graphify-extract/tests/dotnet_xaml.rs, crates/graphify-extract/tests/csharp_type_resolution.rs, crates/graphify-extract/tests/vue_extraction.rs, crates/graphify-extract/tests/parity_languages.rs, crates/graphify-extract/tests/fixtures/*
Adds XAML/WPF extraction and C# type resolution, Vue SFC support, ObjC selector/property parsing improvements, and Metal classification changes, with dispatcher/cache wiring and tests.
Export and wiki rendering
crates/graphify-export/src/obsidian.rs, crates/graphify-export/src/canvas.rs, crates/graphify-export/tests/vault_safety.rs, crates/graphify-wiki/src/generate.rs, crates/graphify-wiki/src/render.rs, crates/graphify-wiki/src/util.rs, crates/graphify-wiki/tests/parity.rs
Obsidian export now uses an ownership manifest and guarded writes; canvas layout uses dynamic per-community columns; wiki output switches to portable Markdown links with resolver-based slugs.
CLI timing, reflect freshness, hooks, LLM endpoints, and docs
src/cli/timer.rs, src/cli/args.rs, src/cli/cluster_only.rs, src/cli/extract.rs, src/cli/dispatch.rs, src/cli/reflect.rs, crates/graphify-reflect/src/lib.rs, crates/graphify-reflect/tests/parity.rs, crates/graphify-hooks/src/platform/common/hooks_json.rs, crates/graphify-hooks/tests/read_hook.rs, crates/graphify-llm/src/*, crates/graphify-llm/tests/*, README.md, USAGE.md, Cargo.toml, graphify-py
Adds timing and missing-only CLI behavior, sidecar-aware reflect freshness, trailing-extension hook detection, LLM base-URL overrides and non-streaming requests, and updates docs, version, and submodule pointer.

Sequence Diagram(s)

sequenceDiagram
  participant extract_xaml
  participant code_behind_resolver
  participant viewmodel_resolver
  participant cs_class_lookup
  participant toolkit_extractor
  participant XamlBuilder
  extract_xaml->>code_behind_resolver: resolve x:Class and event handlers
  extract_xaml->>viewmodel_resolver: detect DataContext / Prism / naming
  viewmodel_resolver->>cs_class_lookup: scan *ViewModel classes
  extract_xaml->>toolkit_extractor: emit ObservableProperty / RelayCommand members
  extract_xaml->>XamlBuilder: deduplicate nodes and edges
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~150 minutes

Possibly related PRs

  • bunkerlab-net/graphify#8: Directly expands resolve_seed in crates/graphify-affected with path handling and disambiguation that this PR extends.
  • bunkerlab-net/graphify#25: Introduces ensure_named_node in crates/graphify-extract/src/generic/graph.rs; this PR changes that helper’s signature and stub behavior.
  • bunkerlab-net/graphify#26: Touches the same graphify-reflect freshness check and CLI plumbing that this PR extends with GraphPaths.

Poem

🐇 Hop, hop — new paths are in the tree,
Old names learned to travel carefully.
XAML, Vue, and links now shine,
Timing bells and manifest lines.
The rabbit nods: “A tidy spring!”

🚥 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 PR as a resync to graphify-py commit 92e682f and version 0.9.0.
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 findings from the second CodeRabbit pass. Genuine bugs and
determinism gaps are fixed (several are bugs `graphify-py` shares, fixed
per AGENTS.md); findings that faithfully mirror `graphify-py` are kept,
with the parity reasoning recorded in code.

Fixes:

- `with_xaml_extract_root` restores the thread-local extract root through
  an RAII guard, so a panic can't leak it to a reused rayon worker
  (parity with graphify-py's `try`/`finally`).
- `.xaml` joins the AST disk-cache bypass list: XAML resolution scans
  sibling `.cs` code-behind, so a content-keyed cache served stale
  `ViewModel` members across runs.
- `origin_file` is relativized alongside `source_file` (extraction and
  build) so the #1462 stub field no longer leaks absolute machine paths
  into graph JSON.
- objc and PowerShell `ensure_named_node` emit sourceless stubs (empty
  `source_file`, `origin_file` set) so cross-file references rewire onto
  real definitions instead of becoming phantom duplicates (#1402/#1462).
- Obsidian `owned_write` refuses to follow a symlink at the target or a
  parent component, so a symlinked note can't escape the vault (#1506).
- `find_node` matches a source path by its normalized full path rather
  than tokenized form, so distinct paths no longer collide.
- `cluster-only --missing-only` preserves a malformed labels file instead
  of relabeling over it, matching the non-`--missing-only` path.
- `#[must_use]` on the pure ID/stem helpers; corrected `extract` and
  `label` doc-comments.

Kept as parity (documented in code): the objc compound-selector node-ID
form, C# nested-namespace registration deferral, and the wiki/`index_md`
god-node list all mirror `graphify-py` exactly.

Test strengthening: `lessons_fresh` now covers both graph sidecars
(matching the Python parametrization), the trailing-slash seed test
exercises the re-keyed file node, and the affected test suites return
`Result` with `?` per the repo convention.

By the will of the Machine God
coderabbitai[bot]

This comment was marked as resolved.

A `src\foo.rs` query reached the `source_exact` tier (its `query_path`
normalizes `\` to `/`), but the L1 file-node preference never fired.
`query_basename` came from `Path::file_name` on the raw label, and on
Unix `\` is not a path separator, so the basename kept the whole
backslash string instead of `foo.rs` — so the file node never floated
ahead of its symbols. Derive both `query_basename` and `query_path`
from one slash-normalized string so Windows-style path queries match
the forward-slash behavior (#1503).

The `#[must_use]` nitpick on `find_node` was already satisfied; no
change there.

Ave Deus Mechanicus
Three CodeRabbit findings (C# nested namespaces, ObjC selector colon
collapse, wiki `index_md` god-node list) were re-raised. Verified each
against `graphify-py @ 92e682f`: all faithfully mirror the reference,
none is a still-valid bug, and "fixing" any would break byte-identical
parity. No behavioral change — only inline parity notes:

- ObjC (`objc.rs`): graphify-py joins selector identifiers colon-less
  (`extract.py:9623`/`:9657`), pinned by `objc_compound_selector_call_
  resolves`. Firmed the comment to name the deliberate collision-risk
  trade-off (`setFoo:bar:`/`setFoobar:` collapse); preserving `:` would
  re-key every keyword selector and break parity.
- Wiki (`generate.rs`): `index_md` intentionally gets the full
  `god_nodes_data` (`wiki.py:333`); a node absent from the graph is
  rendered as plain text by `md_link`, not a broken link, so filtering
  would diverge from byte-identical `index.md`. Documented at the call
  site.

The C# site was already documented (`csharp.py:106-111`, flat `own_ns`
with `# len > 1: skip`); left unchanged.

By the will of the Machine God
`cluster-only --no-label --missing-only` with an existing
`.graphify_labels.json` wiped every community to a `Community N`
placeholder, clobbering hand-curated names. The `--no-label` branch
fired before the `--missing-only` handling and never loaded the
existing file.

graphify-py preserves here: `__main__.py:3489` loads existing labels
for any `exists && !force_relabel` case, with no `missing_only`
exclusion. The Rust port had added `&& !opts.missing_only` to that
branch, dropping `--no-label --missing-only` into the placeholder path.

Fire the preserve branch whenever no LLM naming will happen
(`!missing_only || no_label`), since `--no-label` forbids any LLM call.
Curated labels survive; only true gaps fall back to placeholders. Adds
a `cluster-only` regression test.

By the will of the Machine God
Address the latest CodeRabbit findings (verified against
`graphify-py @ 92e682f`):

- `cluster-only` marked the `export` stage before the HTML render, so
  `--timing` excluded `to_html` time. graphify-py marks it after
  (`__main__.py:3555`); moved the mark past the render to match.
- Strengthened the label-preservation tests to assert community 0
  still maps to `Authentication` by key (parse + `["0"]`), not a bare
  substring; added a concrete per-stage line to the timing test. Adds
  `serde_json` as a dev-dependency for the structural assertion.

Skipped (parity): the `--no-label` branch keeps `&& !force_relabel` —
`label` does not accept `--no-label`, so the combination is
unreachable, and the guard matches graphify-py `__main__.py:3494`
(`elif no_label and not force_relabel`).

By the will of the Machine God
@rblaine95 rblaine95 force-pushed the resync/graphify-py/92e682f branch from 7985376 to 6bf2f0d Compare June 29, 2026 15:41
@rblaine95 rblaine95 merged commit 07fb98b into master Jun 29, 2026
14 checks passed
@rblaine95 rblaine95 deleted the resync/graphify-py/92e682f branch June 29, 2026 16:03
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