Skip to content

Fix/thread project root into verification#194

Open
shrutu0929 wants to merge 2 commits into
Refactron-ai:mainfrom
shrutu0929:fix/thread-project-root-into-verification
Open

Fix/thread project root into verification#194
shrutu0929 wants to merge 2 commits into
Refactron-ai:mainfrom
shrutu0929:fix/thread-project-root-into-verification

Conversation

@shrutu0929
Copy link
Copy Markdown

@shrutu0929 shrutu0929 commented May 15, 2026

solve #187
AutoFixEngine.fix_file() previously constructed its verification engine with VerificationEngine(project_root=file_path.parent), hardcoding the project root to the directory of the file being fixed.
This is incorrect for monorepos and any layout where the test suite or project configuration lives in a directory above the edited file — TestSuiteGate._find_relevant_tests would search from the wrong
root and miss the relevant tests, and the behavior diverged from refactron verify --project-root, which already accepts an explicit root.

This change adds an optional project_root parameter to fix_file(). When a caller passes a root explicitly, it is forwarded unchanged to VerificationEngine, so callers that already know the correct
root (such as RefactronPipeline) can ensure verification runs against it. When no root is supplied, fix_file() now discovers one by walking up from the file to the nearest ancestor containing a VCS or
project marker (.git, .hg, .svn, pyproject.toml, setup.py, or setup.cfg), falling back to the file's own directory only when no marker is found. The discovery logic is implemented in a new
_discover_project_root() helper in the same module.

The change is accompanied by a new test file, tests/test_fix_file_project_root.py, with six tests covering root discovery via .git and pyproject.toml, the fallback to the file directory when no marker
exists, explicit pass-through of project_root to VerificationEngine, and confirmation that discovery resolves to the repo root rather than file_path.parent. All new and related tests pass, and the
changed files are clean under black, isort, and flake8.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added optional project_root parameter to file fixing operations for explicit project root configuration during verification.
  • Performance Improvements

    • Test file discovery results are now cached, reducing repeated filesystem scans during verification runs and improving performance.

Review Change Stack

shrutu0929 and others added 2 commits April 7, 2026 18:31
AutoFixEngine.fix_file() always constructed
VerificationEngine(project_root=file_path.parent), which breaks
monorepos and layouts where tests/config live above the edited file.

- Add optional project_root parameter to fix_file().
- When omitted, discover the project/VCS root by walking up from the
  file to the nearest .git/.hg/.svn or pyproject.toml/setup.py/setup.cfg
  marker, falling back to the file directory.
- Add _discover_project_root() helper and tests covering discovery,
  fallback, and explicit pass-through to VerificationEngine.

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

coderabbitai Bot commented May 15, 2026

📝 Walkthrough

Walkthrough

The PR enhances the verification system by adding intelligent project-root discovery to avoid hardcoded assumptions about repository structure. AutoFixEngine.fix_file() now accepts an optional project_root parameter and discovers the actual project root by walking upward for VCS/project markers. TestSuiteGate independently improves performance by caching test file discovery across multiple verify() invocations. New tests validate project-root discovery and wiring.

Changes

Verification System Enhancements

Layer / File(s) Summary
Project root discovery and threading
refactron/autofix/engine.py
New _discover_project_root() helper locates the nearest .git, pyproject.toml, or other project marker by walking upward, with fallback to the file's directory. AutoFixEngine.fix_file() signature adds optional project_root: Optional[Path] = None parameter. Verification now instantiates VerificationEngine with the resolved or supplied project root instead of always using file_path.parent. Docstring updated to document monorepo and nested layout behavior.
Test file discovery caching
refactron/verification/checks/test_gate.py
TestSuiteGate.__init__ initializes _test_file_cache and _all_test_files caches. _find_relevant_tests() lazily builds a precomputed list of test files from tests/ or test/ directories (skipping VCS and virtualenv directories), then returns cached per-module results instead of rescanning the filesystem on each verify() call.
Project root discovery test coverage
tests/test_fix_file_project_root.py
New test suite validates _discover_project_root can walk to VCS markers, recognize pyproject.toml, and fall back to the file's directory. Monkeypatched VerificationEngine recording verifies fix_file(verify=True) propagates explicit project_root unchanged and discovers the repository root when none is supplied. Tests confirm _discover_project_root is importable from the engine module.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

Poem

🐰 A rabbit hops up, up, up the tree,
Seeking the root where all tests be free—
No more scanning twice, no more parent play,
Discovery caches save the day! 🌳✨

🚥 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 PR title 'Fix/thread project root into verification' directly describes the main change: threading the project root parameter through to the verification engine for correct behavior in monorepos.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 and usage tips.

Copy link
Copy Markdown
Contributor

@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: 3

🧹 Nitpick comments (1)
refactron/autofix/engine.py (1)

28-28: 💤 Low value

Consider a more robust file-vs-directory check.

The suffix-based check (file_path.suffix) assumes paths without extensions are directories, which may not always hold. While this works for typical Python files (.py suffix present), edge cases like extension-less executables or config files could behave unexpectedly.

If file_path is guaranteed to exist at call time, file_path.is_file() would be more reliable. Otherwise, the current approach is acceptable with a comment noting the assumption.

♻️ Alternative approach (if file existence is guaranteed)
-    start = file_path.parent if file_path.suffix else file_path
+    start = file_path.parent if file_path.is_file() else file_path
🤖 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 `@refactron/autofix/engine.py` at line 28, The current assignment "start =
file_path.parent if file_path.suffix else file_path" uses suffix to detect
files; replace this with a more robust check using file_path.is_file() (i.e.,
"start = file_path.parent if file_path.is_file() else file_path") if file_path
is guaranteed to exist at call time, or, if existence cannot be assumed, leave
the logic but add a clarifying comment near the "start" assignment explaining
the suffix-based assumption and why it's acceptable; refer to the symbol
file_path and the variable start in your change.
🤖 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 `@refactron/autofix/engine.py`:
- Line 17: The module constant _PROJECT_ROOT_MARKERS is missing a type
annotation; add an explicit annotation such as using typing.Tuple[str, ...]
(e.g. _PROJECT_ROOT_MARKERS: Tuple[str, ...]) and ensure you import Tuple from
typing at the top of refactron/autofix/engine.py so the constant is statically
typed and satisfies mypy's disallow_untyped_defs rule.

In `@refactron/verification/checks/test_gate.py`:
- Around line 118-123: Remove the trailing whitespace on the changed blank lines
in the test discovery block around the variables test_dirs, search_dirs, and
excluded_dirs (the hunk containing "test_dirs = ...", "search_dirs = ...",
"excluded_dirs = ...", and the "for root_dir in search_dirs:" line) to satisfy
the pre-commit trailing-whitespace hook; also remove trailing spaces on the
other reported blank lines in the same file near the test_gate.py hunk (the
changes around the regions reported as 130-131 and 144-145) so no blank lines
contain trailing spaces.
- Around line 112-113: The cache key for _test_file_cache uses module_name =
file_path.stem which collides for different files with the same stem; change the
key to use a unique path-based identifier instead (e.g., compute module_key from
file_path.relative_to(self.project_root).as_posix() when self.project_root is
set, otherwise use file_path.resolve().as_posix()) and replace uses of
module_name for cache lookups/assignments with module_key; apply the same change
to the other occurrences referenced around the _test_file_cache usage (the
blocks where module_name is set at the other locations).

---

Nitpick comments:
In `@refactron/autofix/engine.py`:
- Line 28: The current assignment "start = file_path.parent if file_path.suffix
else file_path" uses suffix to detect files; replace this with a more robust
check using file_path.is_file() (i.e., "start = file_path.parent if
file_path.is_file() else file_path") if file_path is guaranteed to exist at call
time, or, if existence cannot be assumed, leave the logic but add a clarifying
comment near the "start" assignment explaining the suffix-based assumption and
why it's acceptable; refer to the symbol file_path and the variable start in
your change.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 91d446ca-3401-46ef-b91d-bb82b9ceb211

📥 Commits

Reviewing files that changed from the base of the PR and between 2f04295 and 5ccd6a4.

📒 Files selected for processing (3)
  • refactron/autofix/engine.py
  • refactron/verification/checks/test_gate.py
  • tests/test_fix_file_project_root.py


# Directory markers used to locate the project/VCS root when no explicit
# root is supplied to fix_file().
_PROJECT_ROOT_MARKERS = (".git", ".hg", ".svn", "pyproject.toml", "setup.py", "setup.cfg")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add type annotation to module constant.

The constant _PROJECT_ROOT_MARKERS lacks a type annotation. As per coding guidelines, type annotations are required in refactron/ with mypy disallow_untyped_defs = true enabled.

📝 Proposed fix
+from typing import Dict, List, Optional, Tuple
+
 # Directory markers used to locate the project/VCS root when no explicit
 # root is supplied to fix_file().
-_PROJECT_ROOT_MARKERS = (".git", ".hg", ".svn", "pyproject.toml", "setup.py", "setup.cfg")
+_PROJECT_ROOT_MARKERS: Tuple[str, ...] = (
+    ".git", ".hg", ".svn", "pyproject.toml", "setup.py", "setup.cfg"
+)
🤖 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 `@refactron/autofix/engine.py` at line 17, The module constant
_PROJECT_ROOT_MARKERS is missing a type annotation; add an explicit annotation
such as using typing.Tuple[str, ...] (e.g. _PROJECT_ROOT_MARKERS: Tuple[str,
...]) and ensure you import Tuple from typing at the top of
refactron/autofix/engine.py so the constant is statically typed and satisfies
mypy's disallow_untyped_defs rule.

Comment on lines 112 to 113
module_name = file_path.stem
search_root = self.project_root or file_path.parent
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Cache key collision can return wrong test files for same-stem modules.

module_name = file_path.stem is too coarse for _test_file_cache. Two different files with the same stem will share cache entries and can reuse unrelated test selections.

💡 Proposed fix
-        module_name = file_path.stem
+        module_name = file_path.stem
+        cache_key = str(file_path.resolve())
         search_root = self.project_root or file_path.parent
@@
-        if module_name in self._test_file_cache:
-            return self._test_file_cache[module_name]
+        if cache_key in self._test_file_cache:
+            return self._test_file_cache[cache_key]
@@
-        self._test_file_cache[module_name] = test_files
+        self._test_file_cache[cache_key] = test_files
         return test_files

Also applies to: 131-133, 145-146

🤖 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 `@refactron/verification/checks/test_gate.py` around lines 112 - 113, The cache
key for _test_file_cache uses module_name = file_path.stem which collides for
different files with the same stem; change the key to use a unique path-based
identifier instead (e.g., compute module_key from
file_path.relative_to(self.project_root).as_posix() when self.project_root is
set, otherwise use file_path.resolve().as_posix()) and replace uses of
module_name for cache lookups/assignments with module_key; apply the same change
to the other occurrences referenced around the _test_file_cache usage (the
blocks where module_name is set at the other locations).

Comment on lines +118 to +123

test_dirs = [d for d in [search_root / "tests", search_root / "test"] if d.is_dir()]
search_dirs = test_dirs if test_dirs else [search_root]
excluded_dirs = {".git", ".rag", "__pycache__", "venv", ".venv", "env", "node_modules"}

for root_dir in search_dirs:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove trailing whitespace in this hunk to unblock pre-commit.

Pre-commit is currently failing on trailing-whitespace, and these changed blank lines appear to be part of that failure.

Also applies to: 130-131, 144-145

🤖 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 `@refactron/verification/checks/test_gate.py` around lines 118 - 123, Remove
the trailing whitespace on the changed blank lines in the test discovery block
around the variables test_dirs, search_dirs, and excluded_dirs (the hunk
containing "test_dirs = ...", "search_dirs = ...", "excluded_dirs = ...", and
the "for root_dir in search_dirs:" line) to satisfy the pre-commit
trailing-whitespace hook; also remove trailing spaces on the other reported
blank lines in the same file near the test_gate.py hunk (the changes around the
regions reported as 130-131 and 144-145) so no blank lines contain trailing
spaces.

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