Skip to content

fix: report all projects when multiple share the same sourceRoot#39

Merged
EladBezalel merged 3 commits intofrontops-dev:mainfrom
nirsky:fix/shared-source-root-all-projects-affected
Mar 19, 2026
Merged

fix: report all projects when multiple share the same sourceRoot#39
EladBezalel merged 3 commits intofrontops-dev:mainfrom
nirsky:fix/shared-source-root-all-projects-affected

Conversation

@nirsky
Copy link
Copy Markdown
Collaborator

@nirsky nirsky commented Mar 19, 2026

Summary

Fixes #38

  • Replace get_package_name_by_path (.find() → first match only) with get_package_names_by_path (.filter() → all matches) so that every project whose sourceRoot contains a changed file is reported as affected
  • Update all 5 call sites in core.rs to iterate over all matching projects for source files, asset files, asset references, and cross-file symbol references
  • Add unit test for shared sourceRoot matching and integration test reproducing the exact scenario from the issue (two variant-build projects sharing one source directory)

Test plan

  • cargo test --lib — 82 unit tests pass including new test_get_package_names_by_path_shared_source_root
  • cargo test --test integration_test --no-default-features test_shared_source_root — new integration test passes, both app-desktop and app-desktop-mv3 are correctly reported
  • cargo clippy --all-targets --no-default-features — no warnings
  • cargo fmt --check — clean

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Change-impact detection now attributes changes to all projects that share a source root, ensuring edits to shared files or symbols mark every owning/importing project as affected.
    • Causality tracking updated to record imported-symbol and direct-change causes from multiple source projects where applicable.
  • Tests

    • Added/updated tests validating multi-project scenarios with shared source roots and vectorized lookup behavior.

…ntops-dev#38)

The file-to-project mapping used `.find()` which returns only the first
match, causing non-deterministic results when multiple Nx projects share
a sourceRoot (e.g., variant builds like MV2 vs MV3). Replace with
`.filter()` to return all matching projects at every call site.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 19, 2026

📝 Walkthrough

Walkthrough

Replaced single-project ownership resolution with a ProjectIndex that returns all projects whose sourceRoot matches a file path; updated affected-package discovery and causality recording to iterate over all matching projects. Added unit and integration tests validating multi-owner behavior.

Changes

Cohort / File(s) Summary
Core causality & processing
src/core.rs
Replaced per-call project slices with ProjectIndex usage; switched calls to get_package_names_by_path(...) and iterate over all matching project names when inserting affected packages and recording causes. Adjusted process_changed_symbol to iterate source projects and clone symbols when storing causes.
Project index and lookup utilities
src/utils.rs
Added pub struct ProjectIndex with new(...) and get_package_names_by_path(...) -> Vec<String>; removed single-match get_package_name_by_path(...). Lookup now collects all projects whose source_root is a prefix of the file path; unit tests updated/added for single, none, and multiple-match cases.
Integration tests
tests/integration_test.rs
Added test_shared_source_root_all_projects_affected which creates a temp repo, sets up two projects sharing the same source_root, modifies a file, runs find_affected, and asserts both projects are reported affected.

Sequence Diagram(s)

sequenceDiagram
    participant FileChange
    participant ProjectIndex
    participant Causality
    rect rgba(255, 0, 0, 0.5)
    Note over FileChange,Causality: Old Behavior (single-owner)
    FileChange->>ProjectIndex: query (old single-match) for file
    ProjectIndex-->>FileChange: returns one project or none
    FileChange->>Causality: record affect for single project
    end

    rect rgba(0, 255, 0, 0.5)
    Note over FileChange,Causality: New Behavior (multi-owner)
    FileChange->>ProjectIndex: get_package_names_by_path(file_path)
    ProjectIndex-->>FileChange: returns Vec of matching projects
    loop for each project in Vec
        FileChange->>Causality: record affect / record ImportedSymbol cause for project
    end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through roots and found them paired,
No longer will a twin be spared.
I list them all, each name in sight,
Now every variant sees the light.
A tiny hop — all matches right!

🚥 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 and specifically describes the main change: fixing the issue where multiple projects sharing the same sourceRoot now report all affected projects instead of just one.
Linked Issues check ✅ Passed The PR fully addresses issue #38 by implementing multi-project tracking for shared sourceRoots through ProjectIndex, replacing single-match behavior with all-matches behavior across all affected call sites.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the shared sourceRoot issue: ProjectIndex introduction, get_package_names_by_path replacement, core.rs integration, and comprehensive test coverage.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/integration_test.rs`:
- Around line 2186-2191: The test is missing local git user config before
committing; call git_in to set user.email and user.name on the test repo (e.g.,
git_in(&root, &["config", "user.email", "test@example.com"]) and git_in(&root,
&["config", "user.name", "Test User"])) before the git_in(&root, &["add", "."])
/ git_in(&root, &["commit", "-m", "initial"]) lines so commits won't fail in CI
without global git config; insert these calls near the existing git_in(...)
sequence in tests/integration_test.rs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a67a1099-f354-4bdb-b6a2-27881071ef5a

📥 Commits

Reviewing files that changed from the base of the PR and between 1667a69 and 6006eb7.

📒 Files selected for processing (3)
  • src/core.rs
  • src/utils.rs
  • tests/integration_test.rs

Comment thread tests/integration_test.rs
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="tests/integration_test.rs">

<violation number="1" location="tests/integration_test.rs:2188">
P2: Missing `git config user.email` and `git config user.name` before `git commit`. Other TempDir-based tests in this file set these after `git init`; without them, `git commit` will fail in CI environments that lack global git config.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread tests/integration_test.rs
Add user.email and user.name config after git init to match other
TempDir-based tests and prevent failures in CI environments without
global git config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 19, 2026

📦 Preview Release Available

A preview release has been published for commit 62fd6e9.

Installation

npm install https://github.com/frontops-dev/domino/releases/download/pr-39-62fd6e9/front-ops-domino-0.7.1.tgz

Running the preview

npx https://github.com/frontops-dev/domino/releases/download/pr-39-62fd6e9/front-ops-domino-0.7.1.tgz affected

Details

Copy link
Copy Markdown
Collaborator

@EladBezalel EladBezalel left a comment

Choose a reason for hiding this comment

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

Review: fix: report all projects when multiple share the same sourceRoot

LGTM — clean, correct fix with good test coverage.

What I checked

  • utils.rs: The .find().filter() change is the right fix. Signature change from Option<String> to Vec<String> is clean and the new unit test for shared sourceRoot covers the key scenario well.

  • core.rs: All 5 call sites correctly updated to iterate over all matching projects. The change from for symbol in symbolsfor symbol in &symbols + .clone() is necessary since symbols must survive across multiple pkg iterations — that's fine.

  • process_changed_symbol: The nested iteration for src_proj in &source_projects inside for pkg in &ref_packages produces a cartesian product of ImportedSymbol causes when both source and reference files are in multiple projects. This is technically correct (each combination is a valid causal chain), but worth noting it can produce verbose reports — e.g., if a source file is in [A, B] and a reference file is in [C, D], you get 4 cause entries. Acceptable for correctness, but if report noise becomes an issue down the road, could be worth deduplicating.

  • Integration test: Solid — reproduces the exact issue #38 scenario with two variant-build projects sharing one source directory. Git user config properly set (fixed in the follow-up commit).

One minor suggestion

The get_package_names_by_path function now always scans all projects (no short-circuit). For most monorepos this is fine since project count is small, but if you ever want to optimize, an index from sourceRoot → Vec<project_name> built once at startup would make lookups O(1) instead of O(n_projects). Not needed now — just a thought for the future.

Verdict

Correct fix, good tests, clean implementation. Ship it. 🚢

Replace per-call O(n_projects) scanning in get_package_names_by_path
with a ProjectIndex that groups project names by sourceRoot at startup,
reducing repeated allocations and comparisons during analysis.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
src/core.rs (2)

96-127: Resolve changed-line symbols once per line in report mode.

analyzer.find_node_at_line(...) now runs once per changed line per owning package. For shared roots, that repeats the same lookup for identical input. Cache the symbols per line before the pkg loop and reuse them when writing direct-change causes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core.rs` around lines 96 - 127, The loop calls
analyzer.find_node_at_line(file_path, line, 0) repeatedly for each owning
package; to fix, when generate_report is true compute and cache the symbols per
changed line once (e.g., build a HashMap<usize, Vec<Symbol>> from
changed_file.changed_lines using analyzer.find_node_at_line) before iterating
owning_packages, then inside the pkg loop reuse that cached_symbols[line] to
push AffectCause::DirectChange into project_causes; reference
changed_file.changed_lines, analyzer.find_node_at_line,
owning_packages/project_index.get_package_names_by_path, and
project_causes/affected_packages for locating where to apply the change.

464-466: Skip source-owner lookup when no report is being built.

source_projects is only consumed in the project_causes branch. In the normal find_affected path this still does a prefix scan and clones names on every recursive symbol walk.

♻️ Suggested guard
-  let source_projects = project_index.get_package_names_by_path(file_path);
+  let source_projects = if state.project_causes.is_some() {
+    project_index.get_package_names_by_path(file_path)
+  } else {
+    Vec::new()
+  };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core.rs` around lines 464 - 466, The code eagerly computes
source_projects via project_index.get_package_names_by_path(file_path) even when
no report is requested; move this call behind the branch that handles
project_causes so it's only executed when project_causes is true (i.e., in the
project_causes code path), and keep the find_affected path unchanged to avoid
the prefix scan and cloning on every recursive symbol walk; update references to
source_projects so they are only used after it's initialized within the
project_causes branch.
src/utils.rs (1)

170-209: Use generic fixtures in this shared-root test.

These names and paths read like repo-specific examples. Please swap them for neutral placeholders so the test stays compliant with the repo’s example-redaction rule.

As per coding guidelines, "Remove references to external repositories, obfuscate repository names, project names, and file paths from examples using generic names."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils.rs` around lines 170 - 209, Update the
test_project_index_shared_source_root fixture to replace repo-specific names and
paths with neutral placeholders: change project names like "app-desktop",
"app-desktop-mv3", and "other-project" to generic names such as "project-a",
"project-b", "project-c", and replace source_root paths like
"projects/app-desktop/src" and "projects/other/src" with generic paths such as
"packages/project-a/src", "packages/project-b/src", and
"packages/project-c/src"; ensure assertions still use the new names and that
calls to ProjectIndex::new and get_package_names_by_path continue to reference
the updated placeholder paths so the test logic (shared vs unique source_root
matches and empty result) remains identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/core.rs`:
- Around line 96-127: The loop calls analyzer.find_node_at_line(file_path, line,
0) repeatedly for each owning package; to fix, when generate_report is true
compute and cache the symbols per changed line once (e.g., build a
HashMap<usize, Vec<Symbol>> from changed_file.changed_lines using
analyzer.find_node_at_line) before iterating owning_packages, then inside the
pkg loop reuse that cached_symbols[line] to push AffectCause::DirectChange into
project_causes; reference changed_file.changed_lines,
analyzer.find_node_at_line,
owning_packages/project_index.get_package_names_by_path, and
project_causes/affected_packages for locating where to apply the change.
- Around line 464-466: The code eagerly computes source_projects via
project_index.get_package_names_by_path(file_path) even when no report is
requested; move this call behind the branch that handles project_causes so it's
only executed when project_causes is true (i.e., in the project_causes code
path), and keep the find_affected path unchanged to avoid the prefix scan and
cloning on every recursive symbol walk; update references to source_projects so
they are only used after it's initialized within the project_causes branch.

In `@src/utils.rs`:
- Around line 170-209: Update the test_project_index_shared_source_root fixture
to replace repo-specific names and paths with neutral placeholders: change
project names like "app-desktop", "app-desktop-mv3", and "other-project" to
generic names such as "project-a", "project-b", "project-c", and replace
source_root paths like "projects/app-desktop/src" and "projects/other/src" with
generic paths such as "packages/project-a/src", "packages/project-b/src", and
"packages/project-c/src"; ensure assertions still use the new names and that
calls to ProjectIndex::new and get_package_names_by_path continue to reference
the updated placeholder paths so the test logic (shared vs unique source_root
matches and empty result) remains identical.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cba3abde-0bb5-4b87-ba77-24313940b9a9

📥 Commits

Reviewing files that changed from the base of the PR and between 653b4d3 and 62fd6e9.

📒 Files selected for processing (2)
  • src/core.rs
  • src/utils.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.

Non-deterministic results when multiple projects share the same sourceRoot

2 participants