Skip to content

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

Open
rblaine95 wants to merge 4 commits into
masterfrom
resync/graphify-py/92e682f
Open

Resync with graphify-py @ 92e682f (v0.9.0)#31
rblaine95 wants to merge 4 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.
  • New Features
    • Added/expanded Vue, Svelte, Astro, and XAML extraction support, including richer cross-file linking (and recognizing .metal as code).
    • Added optional --timing for extract and label/cluster-only flows, plus --missing-only to rename only placeholder communities.
    • Improved Obsidian, Canvas, and wiki export; added support for KIMI/GEMINI/DEEPSEEK base URL environment overrides.
  • Bug Fixes
    • Improved source path matching (including trailing slashes) and better file-node preference to reduce ambiguous results.
    • Updated node IDs for improved stability/collision handling and made wiki links portable; repeated exports now better preserve user content and skip unsafe symlinks.

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
📝 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 clearly summarizes the main change: resyncing with graphify-py at v0.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 coderabbitai 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.

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/graphify-extract/src/extractors/powershell/mod.rs (1)

243-258: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Keep PowerShell type-reference placeholders sourceless.

PsRefCtx::ensure_named_node materializes unresolved type references, but this still records them as if they were real declarations in self.str_path and leaves origin_file empty. Align it with the new stub contract so cross-file resolution can distinguish provenance without treating the reference site as a definition.

Proposed fix
-    fn ensure_named_node(&mut self, name: &str, line: usize) -> String {
+    fn ensure_named_node(&mut self, name: &str, _line: usize) -> String {
         let nid1 = make_id(&[self.stem, name]);
         if self.seen_ids.contains(&nid1) {
             return nid1;
         }
         let nid2 = make_id1(name);
         if self.seen_ids.insert(nid2.clone()) {
             self.nodes.push(Node {
                 id: nid2.clone(),
                 label: name.to_string(),
                 file_type: "code".to_string(),
-                source_file: self.str_path.to_string(),
-                source_location: Some(format!("L{line}")),
+                source_file: String::new(),
+                source_location: None,
                 metadata: None,
-                origin_file: None,
+                origin_file: Some(self.str_path.to_string()),
             });
         }
         nid2
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/graphify-extract/src/extractors/powershell/mod.rs` around lines 243 -
258, Keep unresolved PowerShell type-reference placeholders from being recorded
as real declarations in PsRefCtx::ensure_named_node. Update the placeholder node
creation path so these sourceless references do not use self.str_path as their
source_file and instead carry the new stub provenance contract, with origin_file
populated appropriately to distinguish them from actual definitions. Make the
change in the ensure_named_node flow where Node is pushed for the fallback id.
🧹 Nitpick comments (10)
crates/graphify-build/src/migrate.rs (1)

34-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add #[must_use] to the pure ID helpers. make_id, make_id1, file_stem, old_file_stems, and semantic_id_remap all return owned values with no side effects, so dropped results stay silent here. graph_has_legacy_ids already follows this pattern.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/graphify-build/src/migrate.rs` around lines 34 - 53, Add #[must_use]
to the pure ID helper functions so dropped return values are flagged: update
make_id, make_id1, file_stem, old_file_stems, and semantic_id_remap to use the
same pattern already used by graph_has_legacy_ids. Keep the functions’ behavior
unchanged; only add the attribute on these named helpers so callers are warned
when the returned owned ID values are ignored.

Source: Coding guidelines

crates/graphify-extract/tests/parity_languages.rs (1)

185-191: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer Result-returning tests over repeated expect(...).

These new filesystem tests are correctly isolated with tempfile::tempdir(), but the repeated expect("tempdir") / expect("write") / expect("mkdir") pattern is drifting from the repo rule for tests/**/*.rs. Please switch these tests to return Result and use ? for setup so they stay aligned with the workspace’s clippy/test conventions. As per coding guidelines, tests/**/*.rs: “In test files, prefer ? operator returning Result.”

Also applies to: 202-211, 235-241, 250-256, 282-290, 718-729, 763-774, 791-797, 814-823, 992-996

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/graphify-extract/tests/parity_languages.rs` around lines 185 - 191,
The filesystem tests in `parity_languages.rs` are still using repeated
`expect(...)` for setup, which violates the repo convention for `tests/**/*.rs`.
Update the affected test functions to return `Result` and replace `tempdir`,
`write`, `mkdir`, and similar setup `expect` calls with `?`, keeping the test
logic in the existing helper/test names so the setup failures propagate cleanly.

Source: Coding guidelines

crates/graphify-extract/tests/csharp_type_resolution.rs (1)

3-17: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use Result-returning tests instead of blanket unwrap/expect allowances.

This suite opts out of clippy at file scope and then uses unwrap/expect throughout. The test guideline here is to prefer ? and keep any unavoidable allowance narrow, so this pattern will spread the wrong default if it lands as-is.

Proposed pattern
-#![allow(clippy::expect_used, clippy::unwrap_used)]
+use std::error::Error;

-fn write_file(root: &Path, rel: &str, text: &str) -> PathBuf {
+fn write_file(root: &Path, rel: &str, text: &str) -> std::io::Result<PathBuf> {
     let p = root.join(rel);
-    std::fs::create_dir_all(p.parent().expect("parent")).expect("create_dir_all");
-    std::fs::write(&p, text).expect("write");
-    p
+    if let Some(parent) = p.parent() {
+        std::fs::create_dir_all(parent)?;
+    }
+    std::fs::write(&p, text)?;
+    Ok(p)
 }

 #[test]
-fn csharp_cross_file_inherits_resolves_to_real_def() {
-    let tmp = tempfile::tempdir().unwrap();
+fn csharp_cross_file_inherits_resolves_to_real_def() -> Result<(), Box<dyn Error>> {
+    let tmp = tempfile::tempdir()?;
     // ...
-    let core = write_file(...);
+    let core = write_file(...)?;
     // ...
+    Ok(())
 }

As per coding guidelines, tests/**/*.rs: "In test files, prefer ? operator returning Result."

Also applies to: 49-52, 76-77, 119-120, 138-139, 179-180, 218-219, 252-253

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/graphify-extract/tests/csharp_type_resolution.rs` around lines 3 - 17,
Replace the file-wide clippy allowance and the `unwrap`/`expect`-based test
style in `csharp_type_resolution.rs` with `Result`-returning tests that use `?`
for fallible setup and assertions. Update `write_file` and the affected test
cases to propagate errors instead of calling `expect`, and keep any clippy
allowance narrowly scoped only if truly needed. Use the test functions and
helper `write_file` as the main places to apply this pattern.

Source: Coding guidelines

crates/graphify-export/tests/vault_safety.rs (1)

7-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid the file-wide unwrap/expect suppression here.

This test file can return Result from each test and use ?; the blanket allow weakens the lint signal more broadly than necessary. As per coding guidelines, in test files prefer ? operator returning Result, and the file-top #![allow(clippy::expect_used, clippy::unwrap_used)] pattern is reserved for tests/parity.rs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/graphify-export/tests/vault_safety.rs` around lines 7 - 12, Remove the
file-wide Clippy suppression for unwrap/expect in this test module and update
the tests in vault_safety.rs to return Result so they can use the ? operator
instead of unwrap/expect. Keep only the narrow lint allowances that are still
needed for this file, and follow the existing test style used elsewhere rather
than the blanket allow pattern reserved for parity tests.

Source: Coding guidelines

crates/graphify-build/src/ingest.rs (1)

308-325: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Mark snapshot_source_files as #[must_use].

This helper is pure and returns an owned map, so it should be annotated #[must_use] to match the workspace Rust rules.

As per coding guidelines, "Use #[must_use] attribute on pure functions returning owned values."

♻️ Proposed fix
+#[must_use]
 fn snapshot_source_files(graph: &Graph) -> IndexMap<String, String> {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/graphify-build/src/ingest.rs` around lines 308 - 325, Mark the pure
helper `snapshot_source_files` with `#[must_use]` so callers are warned when the
returned `IndexMap<String, String>` is ignored. Add the attribute directly on
the function definition in `ingest.rs`, keeping the existing behavior unchanged
and matching the workspace rule for pure functions returning owned values.

Source: Coding guidelines

crates/graphify-extract/src/extractors/svelte.rs (1)

394-421: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Mark vue_mask_non_script as #[must_use].

This helper is pure and returns owned data, so it should carry #[must_use] per the Rust workspace rules.

As per coding guidelines, "Use #[must_use] attribute on pure functions returning owned values."

♻️ Proposed fix
+#[must_use]
 fn vue_mask_non_script(src: &str) -> (String, Option<String>) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/graphify-extract/src/extractors/svelte.rs` around lines 394 - 421,
Mark the pure helper `vue_mask_non_script` with `#[must_use]` so callers are
warned when its returned masked source and language value are ignored. Add the
attribute directly on the function declaration in the Svelte extractor module,
keeping the existing behavior unchanged. This should align with the workspace
rule for pure functions returning owned values.

Source: Coding guidelines

tests/cli_commands.rs (1)

881-899: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use Result + ? in these new integration tests.

The new test paths are all fallible filesystem operations, so fn ...() -> Result<(), _> reads better than stacking unwrap()s and aligns with the repo’s test guidance. As per coding guidelines, tests/**/*.rs: “In test files, prefer ? operator returning Result.” Based on learnings, this workspace avoids .unwrap() usage.

Also applies to: 901-930

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/cli_commands.rs` around lines 881 - 899, The new integration test uses
fallible filesystem setup with unwrap chains instead of the repo’s preferred
Result-based style. Update `cluster_only_timing_emits_stage_lines` to return
`Result<(), _>` and propagate the `tempdir`, path creation, and any other
fallible calls with `?` rather than `unwrap()`. Apply the same pattern to the
related new tests in the surrounding block so they follow the existing test
guidance and avoid panics.

Sources: Coding guidelines, Learnings

crates/graphify-reflect/tests/parity.rs (1)

587-596: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer Result-returning tests over new unwrap() calls.

These changed tests are doing fallible tempdir/fs setup already, so ? is the better fit here and matches the repo’s test-file guidance. As per coding guidelines, tests/**/*.rs: “In test files, prefer ? operator returning Result.” Based on learnings, this workspace avoids .unwrap() usage.

Also applies to: 641-682

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/graphify-reflect/tests/parity.rs` around lines 587 - 596, The new test
setup in lessons_fresh_missing_output_is_not_fresh and the other affected tests
still uses unwrap() for fallible tempdir/filesystem operations, which should be
replaced with Result-returning tests. Update the test functions in parity.rs to
return a Result and propagate errors with ? instead of unwrap(), following the
existing test helper pattern and keeping the assertions intact in functions like
lessons_fresh_missing_output_is_not_fresh and the other changed test cases.

Sources: Coding guidelines, Learnings

tests/cli.rs (1)

604-639: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer Result-returning tests here instead of unwrap().

These integration tests already do fallible filesystem setup, so returning Result<()> and using ? keeps failures cleaner and matches the repo’s Rust test style. As per coding guidelines, tests/**/*.rs: “In test files, prefer ? operator returning Result.” Based on learnings, “do not use .unwrap() anywhere” in this workspace.

Also applies to: 643-674

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/cli.rs` around lines 604 - 639, The test
`explain_source_file_path_prefers_file_level_node` still uses `unwrap()` for
fallible setup and assertions, which conflicts with the repo’s test style.
Change this test to return `Result<()>` and replace the `tempfile::tempdir`,
`fs::write`, and any other fallible calls with `?`, following the same pattern
used in nearby tests in `tests/cli.rs`; keep the existing assertions and add the
final `Ok(())` return. Also apply the same `Result`-returning, no-`unwrap()`
cleanup to the related tests referenced in this block.

Sources: Coding guidelines, Learnings

crates/graphify-extract/src/postprocess.rs (1)

39-50: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Mark the helper as #[must_use].

node_disambiguation_source_key is a pure helper returning an owned String, so it should be annotated to prevent accidental dropped results. As per coding guidelines, "Use #[must_use] attribute on pure functions returning owned values".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/graphify-extract/src/postprocess.rs` around lines 39 - 50, Add
`#[must_use]` to `node_disambiguation_source_key` in `postprocess.rs`. This
helper is a pure function returning an owned `String`, so annotate it to prevent
callers from accidentally ignoring the result. Keep the function behavior
unchanged and apply the attribute directly to the
`node_disambiguation_source_key` definition alongside the existing helper
documentation.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/graphify-affected/tests/parity.rs`:
- Around line 381-384: The assertion in the parity test is still checking the
pre-migration file-node ID instead of the ID produced after
`load_graph()`/`build_from_json()`. Update the `resolve_seed` expectation for
`app/api/example/route.ts/` to use the migrated node ID documented by the
sibling test, and keep the test aligned with the post-load graph keying
behavior.

In `@crates/graphify-export/src/obsidian.rs`:
- Around line 653-669: Reject symlinked write targets in owned_write before
calling std::fs::write: the current owned-path check only protects against
overwriting unowned files but still allows a symlinked target to escape
output_dir. Update owned_write (and any helper it uses) to inspect the target
with symlink_metadata, reject if the target or any relevant parent component is
a symlink, or otherwise canonicalize and verify the resolved path stays under
the output_dir root before writing. Use the owned_write function and its
target/parent path handling as the place to enforce this.

In `@crates/graphify-extract/src/extractors/dotnet/xaml.rs`:
- Around line 76-80: `with_xaml_extract_root` only restores
`XAML_ACTIVE_EXTRACT_ROOT` after `f()` returns normally, so a panic can leak the
temporary root into later work on the same thread. Update this helper to restore
the previous thread-local value even when `f` unwinds, using an unwind-safe
guard/RAII pattern around the `f()` call and ensuring the original value is
always put back before exit.

In `@crates/graphify-extract/src/extractors/multi/cache.rs`:
- Around line 121-125: The AST cache in multi/cache.rs is returning stale .xaml
results before dependent C# inputs are reconsidered, so the cache key or bypass
rules need to account for ViewModel/member resolution dependencies. Update the
JS_CACHE_BYPASS_SUFFIXES handling or the cache lookup path in the relevant
extractor logic so .xaml either bypasses caching like other dynamic files or
includes sibling C# code-behind inputs in the key, and make sure
clear_xaml_csharp_class_cache() is applied in the path that can still hit cached
AST results.

In `@crates/graphify-extract/src/extractors/multi/csharp.rs`:
- Around line 39-50: Update collect_csharp_scope and the C# extraction flow so
nested namespace_declaration/file_scoped_namespace_declaration blocks are
combined into a single fully qualified namespace instead of separate entries.
Then adjust the fqn_to_id registration logic and any lookup/repointing paths
that consume own_ns so types inside nested namespaces are emitted and resolved
as A.B.T rather than A.T or B.T. Use the existing collect_csharp_scope,
fqn_to_id, and related C# extractor paths to ensure the definition map includes
nested-namespace types consistently.

In `@crates/graphify-extract/src/extractors/objc.rs`:
- Around line 575-603: The Objective-C selector reconstruction in objc.rs is
dropping separator characters, so distinct selectors can collapse into the same
identifier; update the method/declaration parsing around the selector assembly
logic in the code that builds method names from identifier children so colon
structure is preserved before make_id. Apply the same fix in both the method
declaration path and the call-site reconstruction path referenced by the
existing selector concatenation code, using the selector-building logic in the
ObjC extractor rather than raw parts.concat().

In `@crates/graphify-extract/src/types.rs`:
- Around line 22-27: Normalize the new `origin_file` field in `types::...`
before graph serialization, since it can currently emit absolute machine-local
paths and break determinism. Update the same root-based path handling used for
`source_file` in the extraction/JSON output path so `origin_file` is relativized
too, or exclude it from emitted graph data if it should remain internal-only.
Use the `origin_file` field in `types.rs` and the existing normalization logic
around graph emission to keep both fields consistent.

In `@crates/graphify-reflect/tests/parity.rs`:
- Around line 651-682: The `lessons_fresh_false_when_analysis_or_labels_newer`
test only covers the `.graphify_analysis.json` sidecar case, so it does not
verify behavior when `.graphify_labels.json` is newer. Update the
`lessons_fresh` test coverage by either adding a second assertion that sets
`labels` newer than `LESSONS.md`, or split the current test into two focused
tests so both sidecars are explicitly exercised.

In `@crates/graphify-serve/src/graph.rs`:
- Around line 781-818: The new source path matching in graph search is too lossy
because search_tokens(...).join(" ") collapses separators and can treat
different file paths as identical. Update the matching logic in the graph search
routine that builds source_exact, exact, prefix, and substring so that
source_file is compared using a normalized full path string before any
token-based matching. Keep tokenized matching only as a fallback for node label
and ID comparisons, and preserve the preferred/source_exact ordering behavior in
the same flow.

In `@crates/graphify-wiki/src/generate.rs`:
- Around line 75-80: The wiki link resolver in generate.rs is colliding because
`resolver` uses only display text, so duplicate labels and the special `"index"`
entry can overwrite or misroute real articles. Update the resolution logic in
the code around `resolver.insert(...)` and the article-generation flow to use a
stable per-article identifier like `cid`/`nid`, or split special-page entries
into a separate namespace, so community/god-node articles and `index` each
resolve deterministically without clobbering each other.
- Around line 96-127: Keep the index generation aligned with the actual written
god-node articles. In `generate.rs`, the `god_articles` collection is filtered
to only nodes present in `graph`, but `index_md(...)` still uses the original
`god_nodes_data` slice, so update the `index_md` call (or its input) to use the
filtered god-node set derived from the same loop that builds `god_articles`. Use
the existing `god_articles`/`write_god_node_articles` flow and the `index_md`
function to ensure `index.md` only advertises nodes that actually got articles
written.

In `@src/cli/args.rs`:
- Around line 172-178: Update the `label` command help text in `Args` so it no
longer claims every community name is always refreshed, since `missing_only`
changes that behavior. Adjust the variant description associated with `graphify
label` to mention the `--missing-only` option and reflect that only unnamed or
placeholder communities are renamed when it is set.

In `@src/cli/cluster_only.rs`:
- Around line 190-191: The `cluster_only` flow is treating a failed
`read_existing_labels` call as an empty label set when `opts.missing_only` is
enabled, which can cause `existing` to be empty and later overwrite a broken
`.graphify_labels.json` with relabeled data. Update the `existing`
initialization in `cluster_only` to handle `read_existing_labels` errors
explicitly instead of using `unwrap_or_default`, and make the `--missing-only`
path preserve the current labels file on read failure the same way the
non-`--missing-only` path does. Verify the relabel/write logic around the later
community output block only runs when the labels file was successfully read or
intentionally absent.

In `@src/cli/extract.rs`:
- Around line 69-71: The orchestration comment in the CLI entry point is out of
sync with the actual flow because graph export still happens before
run_analysis_phase(). Update the comment near the main orchestration in extract
to match the real execution order, or adjust the code order if the intended flow
is detect → AST → semantic → build → cluster → analyze → export, and keep the
wording aligned with the functions that perform export and analysis.

---

Outside diff comments:
In `@crates/graphify-extract/src/extractors/powershell/mod.rs`:
- Around line 243-258: Keep unresolved PowerShell type-reference placeholders
from being recorded as real declarations in PsRefCtx::ensure_named_node. Update
the placeholder node creation path so these sourceless references do not use
self.str_path as their source_file and instead carry the new stub provenance
contract, with origin_file populated appropriately to distinguish them from
actual definitions. Make the change in the ensure_named_node flow where Node is
pushed for the fallback id.

---

Nitpick comments:
In `@crates/graphify-build/src/ingest.rs`:
- Around line 308-325: Mark the pure helper `snapshot_source_files` with
`#[must_use]` so callers are warned when the returned `IndexMap<String, String>`
is ignored. Add the attribute directly on the function definition in
`ingest.rs`, keeping the existing behavior unchanged and matching the workspace
rule for pure functions returning owned values.

In `@crates/graphify-build/src/migrate.rs`:
- Around line 34-53: Add #[must_use] to the pure ID helper functions so dropped
return values are flagged: update make_id, make_id1, file_stem, old_file_stems,
and semantic_id_remap to use the same pattern already used by
graph_has_legacy_ids. Keep the functions’ behavior unchanged; only add the
attribute on these named helpers so callers are warned when the returned owned
ID values are ignored.

In `@crates/graphify-export/tests/vault_safety.rs`:
- Around line 7-12: Remove the file-wide Clippy suppression for unwrap/expect in
this test module and update the tests in vault_safety.rs to return Result so
they can use the ? operator instead of unwrap/expect. Keep only the narrow lint
allowances that are still needed for this file, and follow the existing test
style used elsewhere rather than the blanket allow pattern reserved for parity
tests.

In `@crates/graphify-extract/src/extractors/svelte.rs`:
- Around line 394-421: Mark the pure helper `vue_mask_non_script` with
`#[must_use]` so callers are warned when its returned masked source and language
value are ignored. Add the attribute directly on the function declaration in the
Svelte extractor module, keeping the existing behavior unchanged. This should
align with the workspace rule for pure functions returning owned values.

In `@crates/graphify-extract/src/postprocess.rs`:
- Around line 39-50: Add `#[must_use]` to `node_disambiguation_source_key` in
`postprocess.rs`. This helper is a pure function returning an owned `String`, so
annotate it to prevent callers from accidentally ignoring the result. Keep the
function behavior unchanged and apply the attribute directly to the
`node_disambiguation_source_key` definition alongside the existing helper
documentation.

In `@crates/graphify-extract/tests/csharp_type_resolution.rs`:
- Around line 3-17: Replace the file-wide clippy allowance and the
`unwrap`/`expect`-based test style in `csharp_type_resolution.rs` with
`Result`-returning tests that use `?` for fallible setup and assertions. Update
`write_file` and the affected test cases to propagate errors instead of calling
`expect`, and keep any clippy allowance narrowly scoped only if truly needed.
Use the test functions and helper `write_file` as the main places to apply this
pattern.

In `@crates/graphify-extract/tests/parity_languages.rs`:
- Around line 185-191: The filesystem tests in `parity_languages.rs` are still
using repeated `expect(...)` for setup, which violates the repo convention for
`tests/**/*.rs`. Update the affected test functions to return `Result` and
replace `tempdir`, `write`, `mkdir`, and similar setup `expect` calls with `?`,
keeping the test logic in the existing helper/test names so the setup failures
propagate cleanly.

In `@crates/graphify-reflect/tests/parity.rs`:
- Around line 587-596: The new test setup in
lessons_fresh_missing_output_is_not_fresh and the other affected tests still
uses unwrap() for fallible tempdir/filesystem operations, which should be
replaced with Result-returning tests. Update the test functions in parity.rs to
return a Result and propagate errors with ? instead of unwrap(), following the
existing test helper pattern and keeping the assertions intact in functions like
lessons_fresh_missing_output_is_not_fresh and the other changed test cases.

In `@tests/cli_commands.rs`:
- Around line 881-899: The new integration test uses fallible filesystem setup
with unwrap chains instead of the repo’s preferred Result-based style. Update
`cluster_only_timing_emits_stage_lines` to return `Result<(), _>` and propagate
the `tempdir`, path creation, and any other fallible calls with `?` rather than
`unwrap()`. Apply the same pattern to the related new tests in the surrounding
block so they follow the existing test guidance and avoid panics.

In `@tests/cli.rs`:
- Around line 604-639: The test
`explain_source_file_path_prefers_file_level_node` still uses `unwrap()` for
fallible setup and assertions, which conflicts with the repo’s test style.
Change this test to return `Result<()>` and replace the `tempfile::tempdir`,
`fs::write`, and any other fallible calls with `?`, following the same pattern
used in nearby tests in `tests/cli.rs`; keep the existing assertions and add the
final `Ok(())` return. Also apply the same `Result`-returning, no-`unwrap()`
cleanup to the related tests referenced in this block.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ce4b49b0-9ac9-4c71-92d5-d429c0e5afc2

📥 Commits

Reviewing files that changed from the base of the PR and between 5cd9fac and 4d0d79a.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (128)
  • Cargo.toml
  • README.md
  • USAGE.md
  • crates/graphify-affected/src/lib.rs
  • crates/graphify-affected/tests/parity.rs
  • crates/graphify-analyze/src/classify.rs
  • crates/graphify-build/src/build_fn.rs
  • crates/graphify-build/src/ingest.rs
  • crates/graphify-build/src/lib.rs
  • crates/graphify-build/src/migrate.rs
  • crates/graphify-build/tests/parity.rs
  • crates/graphify-detect/src/extensions.rs
  • crates/graphify-detect/tests/parity_classify.rs
  • crates/graphify-export/src/canvas.rs
  • crates/graphify-export/src/obsidian.rs
  • crates/graphify-export/tests/vault_safety.rs
  • crates/graphify-extract/src/extractors/apex.rs
  • crates/graphify-extract/src/extractors/bash.rs
  • crates/graphify-extract/src/extractors/blade.rs
  • crates/graphify-extract/src/extractors/dart.rs
  • crates/graphify-extract/src/extractors/dm/dmf.rs
  • crates/graphify-extract/src/extractors/dm/dmi.rs
  • crates/graphify-extract/src/extractors/dm/dmm.rs
  • crates/graphify-extract/src/extractors/dm/source.rs
  • crates/graphify-extract/src/extractors/dotnet/csproj.rs
  • crates/graphify-extract/src/extractors/dotnet/mod.rs
  • crates/graphify-extract/src/extractors/dotnet/razor.rs
  • crates/graphify-extract/src/extractors/dotnet/sln.rs
  • crates/graphify-extract/src/extractors/dotnet/slnx.rs
  • crates/graphify-extract/src/extractors/dotnet/xaml.rs
  • crates/graphify-extract/src/extractors/elixir.rs
  • crates/graphify-extract/src/extractors/fortran.rs
  • crates/graphify-extract/src/extractors/go/mod.rs
  • crates/graphify-extract/src/extractors/go/refs.rs
  • crates/graphify-extract/src/extractors/go/walk.rs
  • crates/graphify-extract/src/extractors/groovy.rs
  • crates/graphify-extract/src/extractors/json_lang.rs
  • crates/graphify-extract/src/extractors/julia/mod.rs
  • crates/graphify-extract/src/extractors/julia/walk.rs
  • crates/graphify-extract/src/extractors/manifest_ingest.rs
  • crates/graphify-extract/src/extractors/markdown.rs
  • crates/graphify-extract/src/extractors/mcp.rs
  • crates/graphify-extract/src/extractors/mod.rs
  • crates/graphify-extract/src/extractors/multi/cache.rs
  • crates/graphify-extract/src/extractors/multi/csharp.rs
  • crates/graphify-extract/src/extractors/multi/mod.rs
  • crates/graphify-extract/src/extractors/objc.rs
  • crates/graphify-extract/src/extractors/pascal/forms.rs
  • crates/graphify-extract/src/extractors/pascal/mod.rs
  • crates/graphify-extract/src/extractors/pascal/package.rs
  • crates/graphify-extract/src/extractors/powershell/manifest.rs
  • crates/graphify-extract/src/extractors/powershell/mod.rs
  • crates/graphify-extract/src/extractors/python_rationale.rs
  • crates/graphify-extract/src/extractors/rust_lang/mod.rs
  • crates/graphify-extract/src/extractors/rust_lang/walk.rs
  • crates/graphify-extract/src/extractors/sql/mod.rs
  • crates/graphify-extract/src/extractors/sql/walk.rs
  • crates/graphify-extract/src/extractors/svelte.rs
  • crates/graphify-extract/src/extractors/terraform.rs
  • crates/graphify-extract/src/extractors/verilog.rs
  • crates/graphify-extract/src/extractors/zig.rs
  • crates/graphify-extract/src/generic/graph.rs
  • crates/graphify-extract/src/generic/inherit/java.rs
  • crates/graphify-extract/src/generic/inherit/kotlin.rs
  • crates/graphify-extract/src/generic/inherit/mod.rs
  • crates/graphify-extract/src/generic/inherit/scala.rs
  • crates/graphify-extract/src/generic/inherit/swift.rs
  • crates/graphify-extract/src/generic/mod.rs
  • crates/graphify-extract/src/generic/references/java.rs
  • crates/graphify-extract/src/generic/walk.rs
  • crates/graphify-extract/src/ids.rs
  • crates/graphify-extract/src/ids_tests.rs
  • crates/graphify-extract/src/lang_configs.rs
  • crates/graphify-extract/src/lib.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/types.rs
  • crates/graphify-extract/tests/cross_file_multi.rs
  • crates/graphify-extract/tests/csharp_type_resolution.rs
  • crates/graphify-extract/tests/dotnet_xaml.rs
  • crates/graphify-extract/tests/file_node_id_spec.rs
  • crates/graphify-extract/tests/fixtures/bindings.xaml
  • crates/graphify-extract/tests/fixtures/sample.metal
  • crates/graphify-extract/tests/fixtures/sample.xaml
  • crates/graphify-extract/tests/fixtures/sample.xaml.cs
  • crates/graphify-extract/tests/fixtures/xaml_viewmodel/App.csproj
  • crates/graphify-extract/tests/fixtures/xaml_viewmodel/ViewModels/DesignViewModel.cs
  • crates/graphify-extract/tests/fixtures/xaml_viewmodel/ViewModels/MainViewModel.cs
  • crates/graphify-extract/tests/fixtures/xaml_viewmodel/ViewModels/PrismOrderViewModel.cs
  • crates/graphify-extract/tests/fixtures/xaml_viewmodel/ViewModels/SettingsViewModel.cs
  • crates/graphify-extract/tests/fixtures/xaml_viewmodel/ViewModels/ToolkitViewModel.cs
  • crates/graphify-extract/tests/fixtures/xaml_viewmodel/Views/DesignView.xaml
  • crates/graphify-extract/tests/fixtures/xaml_viewmodel/Views/ExplicitMainWindow.xaml
  • crates/graphify-extract/tests/fixtures/xaml_viewmodel/Views/PrismOrderView.xaml
  • crates/graphify-extract/tests/fixtures/xaml_viewmodel/Views/SettingsView.xaml
  • crates/graphify-extract/tests/fixtures/xaml_viewmodel/Views/ToolkitView.xaml
  • crates/graphify-extract/tests/parity.rs
  • crates/graphify-extract/tests/parity_languages.rs
  • crates/graphify-extract/tests/parity_postprocess.rs
  • crates/graphify-extract/tests/vue_extraction.rs
  • crates/graphify-hooks/src/platform/common/hooks_json.rs
  • crates/graphify-hooks/tests/read_hook.rs
  • crates/graphify-llm/src/constants.rs
  • crates/graphify-llm/src/deepseek.rs
  • crates/graphify-llm/src/gemini.rs
  • crates/graphify-llm/src/kimi.rs
  • crates/graphify-llm/src/openai_compat.rs
  • crates/graphify-llm/tests/custom_endpoint.rs
  • crates/graphify-llm/tests/openai_compat_http.rs
  • crates/graphify-reflect/src/lib.rs
  • crates/graphify-reflect/tests/parity.rs
  • crates/graphify-serve/src/graph.rs
  • crates/graphify-serve/tests/parity.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
  • graphify-py
  • src/cli/args.rs
  • src/cli/cluster_only.rs
  • src/cli/dispatch.rs
  • src/cli/extract.rs
  • src/cli/mod.rs
  • src/cli/reflect.rs
  • src/cli/timer.rs
  • tests/cli.rs
  • tests/cli_commands.rs

Comment thread crates/graphify-affected/tests/parity.rs
Comment thread crates/graphify-export/src/obsidian.rs
Comment thread crates/graphify-extract/src/extractors/dotnet/xaml.rs Outdated
Comment thread crates/graphify-extract/src/extractors/multi/cache.rs Outdated
Comment on lines +39 to +50
fn collect_csharp_scope(
node: tree_sitter::Node<'_>,
source: &[u8],
own_ns: &mut Vec<String>,
usings: &mut Vec<String>,
aliases: &mut HashMap<String, String>,
) {
match node.kind() {
"namespace_declaration" | "file_scoped_namespace_declaration" => {
if let Some(nm) = node.child_by_field_name("name") {
own_ns.push(nm.utf8_text(source).unwrap_or("").trim().to_string());
}

@coderabbitai coderabbitai Bot Jun 29, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Nested namespace syntax never resolves correctly.

collect_csharp_scope records namespace A { namespace B { ... } } as two separate entries ("A", "B"), but fqn_to_id only registers definitions when a file has exactly one namespace string. That means any type declared under nested namespace blocks is omitted from the definition map, and later lookups try A.T / B.T instead of A.B.T. Cross-file repointing will therefore fail for valid C# nested-namespace files.

Also applies to: 118-124, 232-259

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/graphify-extract/src/extractors/multi/csharp.rs` around lines 39 - 50,
Update collect_csharp_scope and the C# extraction flow so nested
namespace_declaration/file_scoped_namespace_declaration blocks are combined into
a single fully qualified namespace instead of separate entries. Then adjust the
fqn_to_id registration logic and any lookup/repointing paths that consume own_ns
so types inside nested namespaces are emitted and resolved as A.B.T rather than
A.T or B.T. Use the existing collect_csharp_scope, fqn_to_id, and related C#
extractor paths to ensure the definition map includes nested-namespace types
consistently.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Is this resolved/addressed?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Outline =="
ast-grep outline crates/graphify-extract/src/extractors/multi/csharp.rs --view expanded || true

echo
echo "== csharp.rs (top) =="
sed -n '1,260p' crates/graphify-extract/src/extractors/multi/csharp.rs

echo
echo "== csharp.rs (rest) =="
sed -n '261,420p' crates/graphify-extract/src/extractors/multi/csharp.rs

echo
echo "== Search for nested namespace coverage =="
rg -n -C3 'nested namespace|namespace A|namespace B|A\.B|file_scoped_namespace_declaration|namespace_declaration' crates/graphify-extract/tests crates/graphify-extract/src/extractors/multi/csharp.rs

Length of output: 16346


@rblaine95 Not yet — I checked the current PR branch, and this is still not addressed.

Why:

  • collect_csharp_scope still records nested blocks separately instead of composing A.B.
  • fqn_to_id registration still skips files when own_ns.len() > 1.
  • The code now explicitly documents this as deferred for graphify-py parity in crates/graphify-extract/src/extractors/multi/csharp.rs around Line 121.

So the comment should remain open unless you want to intentionally accept that parity tradeoff and treat it as out of scope for this PR.


🧠 Learnings used
Learnt from: rblaine95
Repo: bunkerlab-net/graphify PR: 8
File: crates/graphify-build/src/dedup_label.rs:15-16
Timestamp: 2026-05-24T18:19:10.174Z
Learning: In the bunkerlab-net/graphify Rust workspace, do not use `.unwrap()` anywhere. If you must fail fast (e.g., during static `LazyLock<...>` initialization such as compiling a `Regex`), use `.expect(<descriptive_reason>)` with a clear, human-readable message explaining what assumption is being made and why it should never be false in this code path. When reviewing, do not suggest changing existing `.expect(...)` calls to `.unwrap()` in this repository.

Learnt from: rblaine95
Repo: bunkerlab-net/graphify PR: 8
File: crates/graphify-dedup/src/score.rs:29-31
Timestamp: 2026-05-24T18:19:46.125Z
Learning: In the bunkerlab-net/graphify Rust workspace, do not use `.unwrap()` in non-test code. Use `.expect("...")` only when the failure indicates a logically guaranteed invariant, and the message must clearly explain why it holds. For `LazyLock<regex::Regex>` values created from literal patterns, use `.expect("static <name> regex")` (replace `<name>` with the specific regex label) and add `#[allow(clippy::expect_used)]`; do not use `.unwrap()` even if you add `#[allow(clippy::unwrap_used)]`.

Comment thread crates/graphify-wiki/src/generate.rs
Comment on lines +96 to 127
let mut god_articles: Vec<(String, String)> = Vec::new(); // (node_id, slug)
for node_data in god_nodes_data {
if graph.contains_node(&node_data.id) {
let slug = make_unique_slug(&safe_filename(&node_data.label), &mut used_slugs);
resolver
.entry(node_data.label.clone())
.or_insert(slug.clone());
god_articles.push((node_data.id.clone(), slug));
}
}

// Second pass: render and write each article with the full resolver in hand.
let mut count = 0usize;
let wiki_ctx = WikiCtx {
graph,
labels,
node_community: &node_community,
deg_map: &deg_map,
resolver: &resolver,
output_dir,
};
count += write_community_articles(&wiki_ctx, &filtered, cohesion, &mut used_slugs)?;
count += write_god_node_articles(&wiki_ctx, god_nodes_data, &mut used_slugs)?;
count += write_community_articles(&wiki_ctx, &filtered, cohesion, &community_slugs)?;
count += write_god_node_articles(&wiki_ctx, &god_articles)?;

let index = index_md(
&filtered,
labels,
god_nodes_data,
graph.node_count(),
graph.edge_count(),
&resolver,
);

@coderabbitai coderabbitai Bot Jun 29, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep index.md aligned with the filtered god-node set.

This loop drops missing god_nodes_data entries from god_articles, but index_md(...) still receives the original slice. The index can therefore advertise god nodes that never get an article written.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/graphify-wiki/src/generate.rs` around lines 96 - 127, Keep the index
generation aligned with the actual written god-node articles. In `generate.rs`,
the `god_articles` collection is filtered to only nodes present in `graph`, but
`index_md(...)` still uses the original `god_nodes_data` slice, so update the
`index_md` call (or its input) to use the filtered god-node set derived from the
same loop that builds `god_articles`. Use the existing
`god_articles`/`write_god_node_articles` flow and the `index_md` function to
ensure `index.md` only advertises nodes that actually got articles written.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Is this resolved/addressed?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@rblaine95 Not yet — this still looks valid.

write_god_node_articles(...) uses the filtered god_articles, but index_md(...) is still called with the original god_nodes_data, so index.md can still list god-node articles that were skipped.

Relevant spot:

  • crates/graphify-wiki/src/generate.rs: the index_md(...) call still passes god_nodes_data rather than a filtered set derived from god_articles.

🧠 Learnings used
Learnt from: rblaine95
Repo: bunkerlab-net/graphify PR: 8
File: crates/graphify-build/src/dedup_label.rs:15-16
Timestamp: 2026-05-24T18:19:10.174Z
Learning: In the bunkerlab-net/graphify Rust workspace, do not use `.unwrap()` anywhere. If you must fail fast (e.g., during static `LazyLock<...>` initialization such as compiling a `Regex`), use `.expect(<descriptive_reason>)` with a clear, human-readable message explaining what assumption is being made and why it should never be false in this code path. When reviewing, do not suggest changing existing `.expect(...)` calls to `.unwrap()` in this repository.

Learnt from: rblaine95
Repo: bunkerlab-net/graphify PR: 8
File: crates/graphify-dedup/src/score.rs:29-31
Timestamp: 2026-05-24T18:19:46.125Z
Learning: In the bunkerlab-net/graphify Rust workspace, do not use `.unwrap()` in non-test code. Use `.expect("...")` only when the failure indicates a logically guaranteed invariant, and the message must clearly explain why it holds. For `LazyLock<regex::Regex>` values created from literal patterns, use `.expect("static <name> regex")` (replace `<name>` with the specific regex label) and add `#[allow(clippy::expect_used)]`; do not use `.unwrap()` even if you add `#[allow(clippy::unwrap_used)]`.

Comment thread src/cli/args.rs
Comment thread src/cli/cluster_only.rs
Comment thread src/cli/extract.rs
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 coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/graphify-serve/src/graph.rs (1)

762-807: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Normalize backslashes before deriving the basename.

query_path accepts Windows-style separators, but query_basename does not. A query like src\foo.rs can match source_file while failing the L1 file-node preference because the basename remains src\foo.rs.

Proposed fix
+    let normalized_label_path = label.replace('\\', "/");
     let query_basename = strip_diacritics(
-        std::path::Path::new(label)
+        std::path::Path::new(&normalized_label_path)
             .file_name()
             .and_then(|n| n.to_str())
-            .unwrap_or(label),
+            .unwrap_or(label),
     )
     .to_lowercase();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/graphify-serve/src/graph.rs` around lines 762 - 807, Normalize the
query string for basename extraction in the search logic so Windows-style
backslashes are treated as path separators before computing `query_basename`. In
the matching block that builds `query_basename` and `query_path`, make
`query_basename` use the same slash-normalized input as `query_path` so
`src\foo.rs` can still satisfy the L1 file-node preference in the `source_exact`
branch. Keep the existing `strip_diacritics`, `Path::file_name`, and
`get_norm_label` matching flow intact, only ensure the basename is derived from
normalized separators.
🧹 Nitpick comments (1)
crates/graphify-serve/src/graph.rs (1)

757-833: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add #[must_use] to find_node.

This changed pure function returns an owned Vec<String>, so it should be annotated.

Proposed fix
+#[must_use]
 pub fn find_node(graph: &Graph, label: &str) -> Vec<String> {

As per coding guidelines, “Use #[must_use] attribute on pure functions returning owned values”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/graphify-serve/src/graph.rs` around lines 757 - 833, Add #[must_use]
to find_node so callers don’t accidentally ignore its Vec<String> result; this
is a pure lookup function, so annotate the find_node definition in graph.rs and
keep the existing behavior unchanged. Use the find_node symbol as the target for
the attribute addition.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/graphify-serve/src/graph.rs`:
- Around line 762-807: Normalize the query string for basename extraction in the
search logic so Windows-style backslashes are treated as path separators before
computing `query_basename`. In the matching block that builds `query_basename`
and `query_path`, make `query_basename` use the same slash-normalized input as
`query_path` so `src\foo.rs` can still satisfy the L1 file-node preference in
the `source_exact` branch. Keep the existing `strip_diacritics`,
`Path::file_name`, and `get_norm_label` matching flow intact, only ensure the
basename is derived from normalized separators.

---

Nitpick comments:
In `@crates/graphify-serve/src/graph.rs`:
- Around line 757-833: Add #[must_use] to find_node so callers don’t
accidentally ignore its Vec<String> result; this is a pure lookup function, so
annotate the find_node definition in graph.rs and keep the existing behavior
unchanged. Use the find_node symbol as the target for the attribute addition.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 90d343bb-c734-4f74-896f-a8c54e9ac79e

📥 Commits

Reviewing files that changed from the base of the PR and between 4d0d79a and a690728.

📒 Files selected for processing (22)
  • crates/graphify-affected/tests/parity.rs
  • crates/graphify-build/src/ingest.rs
  • crates/graphify-build/src/migrate.rs
  • crates/graphify-export/src/obsidian.rs
  • crates/graphify-export/tests/vault_safety.rs
  • crates/graphify-extract/src/extractors/dotnet/xaml.rs
  • crates/graphify-extract/src/extractors/multi/cache.rs
  • crates/graphify-extract/src/extractors/multi/csharp.rs
  • crates/graphify-extract/src/extractors/multi/mod.rs
  • crates/graphify-extract/src/extractors/objc.rs
  • crates/graphify-extract/src/extractors/powershell/mod.rs
  • crates/graphify-extract/src/extractors/svelte.rs
  • crates/graphify-extract/src/postprocess.rs
  • crates/graphify-extract/tests/csharp_type_resolution.rs
  • crates/graphify-extract/tests/parity_languages.rs
  • crates/graphify-reflect/tests/parity.rs
  • crates/graphify-serve/src/graph.rs
  • src/cli/args.rs
  • src/cli/cluster_only.rs
  • src/cli/extract.rs
  • tests/cli.rs
  • tests/cli_commands.rs
🚧 Files skipped from review as they are similar to previous changes (17)
  • tests/cli.rs
  • crates/graphify-extract/src/postprocess.rs
  • crates/graphify-affected/tests/parity.rs
  • src/cli/args.rs
  • crates/graphify-reflect/tests/parity.rs
  • crates/graphify-build/src/ingest.rs
  • crates/graphify-extract/src/extractors/multi/csharp.rs
  • crates/graphify-extract/tests/csharp_type_resolution.rs
  • crates/graphify-extract/src/extractors/multi/mod.rs
  • crates/graphify-export/src/obsidian.rs
  • src/cli/extract.rs
  • crates/graphify-export/tests/vault_safety.rs
  • crates/graphify-extract/src/extractors/svelte.rs
  • crates/graphify-extract/src/extractors/dotnet/xaml.rs
  • crates/graphify-extract/tests/parity_languages.rs
  • src/cli/cluster_only.rs
  • crates/graphify-build/src/migrate.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.

1 participant