Perf/replace fixer queue preview#182
Conversation
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 52 minutes and 47 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughA new Changes
Sequence DiagramsequenceDiagram
participant User
participant CLI as CLI (run command)
participant Pipeline as RefactronPipeline
participant Refactron
participant AutoFixEngine
participant FileSystem
User->>CLI: Invoke run with target & options
CLI->>Pipeline: Create instance with project_root
Pipeline->>FileSystem: Load .refactron.yaml (if exists)
Pipeline->>Pipeline: Initialize Refactron with config
Pipeline->>AutoFixEngine: Initialize engine
CLI->>Pipeline: analyze(target, use_incremental)
Pipeline->>Pipeline: Override incremental setting (if provided)
Pipeline->>Refactron: analyze(target)
Refactron->>Refactron: Scan & identify issues
Refactron-->>Pipeline: Return AnalysisResult
CLI->>Pipeline: queue_issues(issues)
Pipeline->>Pipeline: For each issue, find fixer (with cache)
Pipeline->>AutoFixEngine: can_fix(issue) or preview(issue)
AutoFixEngine-->>Pipeline: Return fixer name
Pipeline-->>CLI: Return queued issues with fixers
CLI-->>User: Print summary metrics
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (1)
tests/test_pipeline.py (1)
43-48: Assert the effective incremental component, not just the config flag.This test currently passes even if
RefactronPipeline.analyze(..., use_incremental=True)only mutates config afterRefactronhas initialized its tracker. Add an assertion for the initialized tracker state so the test catches the runtime behavior.Suggested assertion
config_passed = pipeline.refactron.config assert config_passed.enable_incremental_analysis is True + assert pipeline.refactron.incremental_tracker.enabled is True🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_pipeline.py` around lines 43 - 48, The test must assert the runtime tracker was initialized with incremental analysis, not just the config flag: after calling RefactronPipeline.analyze(..., use_incremental=True) add an assertion on the Refactron instance's tracker (pipeline.refactron.tracker) to confirm it reflects incremental mode (e.g., pipeline.refactron.tracker.use_incremental or pipeline.refactron.tracker.is_incremental is True); if tracker may be None assert it is initialized first (is not None) then check the appropriate tracker attribute, using the actual tracker attribute name from the Refactron implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@refactron/cli/run.py`:
- Around line 28-33: RefactronPipeline is being instantiated without the target
project's path so config discovery defaults to the current working directory;
change the code that creates RefactronPipeline() to pass the effective project
root derived from target_path (from _validate_path) — e.g. construct
RefactronPipeline with the project root argument (or call its load/config
initializer) so that pipeline.analyze(target_path, use_incremental=incremental)
picks up /other/project/.refactron.yaml instead of the CWD.
- Around line 35-41: The first console.print uses an f-string with no
placeholders (console.print(f"[green]Pipeline analysis completed
successfully.[/green]"))—replace it with a normal string literal to satisfy
linting (console.print("[green]Pipeline analysis completed
successfully.[/green]")), and in the except block preserve exception chaining by
re-raising SystemExit with the caught exception (use raise SystemExit(1) from e)
so the original traceback (e) is kept; ensure lines remain under the
100-character limit.
In `@refactron/core/pipeline.py`:
- Around line 78-100: The current caching uses issue_type = issue.rule_id or
str(getattr(issue, 'category', ...)) which causes fallback preview matches to be
cached broadly; change the logic in the fixer resolution in resolve/fixer lookup
so that when issue.rule_id is missing you either (a) do not write the fallback
candidate_name into self._fixer_cache for that issue_type, or (b) make the cache
key more specific by including additional issue-specific fields (e.g., a unique
identifier, file/line, or a short snippet from issue.source/statement) before
assigning self._fixer_cache[issue_type] = candidate_name; ensure this applies
only when using the preview-based loop over self.autofix_engine.fixers and keep
the fast-path autofix_engine.can_fix(issue) caching unchanged.
- Line 35: The attribute _fixer_cache is untyped which yields Any; annotate it
as a mapping of fixer keys to optional strings (e.g. Dict[str, Optional[str]])
to satisfy mypy and the Optional[str] return expectations: change the assignment
in the class from self._fixer_cache = {} to self._fixer_cache: Dict[str,
Optional[str]] = {} and add the necessary typing imports (from typing import
Dict, Optional) at the module top if not present.
- Around line 62-64: The code updates only
self.refactron.config.enable_incremental_analysis when use_incremental is
provided but leaves the live tracker unchanged; update the actual
IncrementalAnalysisTracker on the Refactron instance as well. After setting
self.refactron.config.enable_incremental_analysis, either set the existing
tracker’s enabled state (e.g. self.refactron.incremental_tracker.enabled =
use_incremental or call its setter method) or recreate it:
self.refactron.incremental_tracker =
IncrementalAnalysisTracker(enabled=use_incremental) (import
IncrementalAnalysisTracker if needed) so the running tracker matches the new
config.
- Around line 3-19: Remove the duplicated imports of RefactronConfig, Refactron,
and AnalysisResult (keep a single import line for each unique symbol) and update
the RefactronPipeline.__init__ signature so it does not exceed the 100-character
line limit (wrap parameters across multiple lines or use type aliases for
Optional[Union[str, Path]]/config_path) to satisfy black/isort/flake8 formatting
rules.
In `@tests/test_pipeline.py`:
- Around line 105-106: The inline comment above the assertion in
tests/test_pipeline.py is longer than 100 characters; split or reflow that
comment into multiple shorter lines (each <=100 chars) so it complies with
flake8 max-line-length. Locate the long comment that precedes the assertion
using the assertion snippet calls["preview"] == 1 (or the surrounding test
function in test_pipeline.py) and break the sentence into two or more shorter
comment lines or rephrase it to keep each line under the 100-character limit.
---
Nitpick comments:
In `@tests/test_pipeline.py`:
- Around line 43-48: The test must assert the runtime tracker was initialized
with incremental analysis, not just the config flag: after calling
RefactronPipeline.analyze(..., use_incremental=True) add an assertion on the
Refactron instance's tracker (pipeline.refactron.tracker) to confirm it reflects
incremental mode (e.g., pipeline.refactron.tracker.use_incremental or
pipeline.refactron.tracker.is_incremental is True); if tracker may be None
assert it is initialized first (is not None) then check the appropriate tracker
attribute, using the actual tracker attribute name from the Refactron
implementation.
🪄 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: d4293809-ef4b-4f07-9670-28211c91e3d0
📒 Files selected for processing (4)
refactron/cli/main.pyrefactron/cli/run.pyrefactron/core/pipeline.pytests/test_pipeline.py
| target_path = _validate_path(target) if target else Path.cwd() | ||
|
|
||
| with console.status("[primary]Executing pipeline run...[/primary]"): | ||
| try: | ||
| pipeline = RefactronPipeline() | ||
| result = pipeline.analyze(target_path, use_incremental=incremental) |
There was a problem hiding this comment.
Load pipeline config from the requested target project.
RefactronPipeline() defaults config discovery to the current working directory, so refactron run /other/project can ignore /other/project/.refactron.yaml. Pass the effective project root derived from target_path.
Proposed fix
target_path = _validate_path(target) if target else Path.cwd()
+ project_root = target_path if target_path.is_dir() else target_path.parent
with console.status("[primary]Executing pipeline run...[/primary]"):
try:
- pipeline = RefactronPipeline()
+ pipeline = RefactronPipeline(project_root=project_root)
result = pipeline.analyze(target_path, use_incremental=incremental)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| target_path = _validate_path(target) if target else Path.cwd() | |
| with console.status("[primary]Executing pipeline run...[/primary]"): | |
| try: | |
| pipeline = RefactronPipeline() | |
| result = pipeline.analyze(target_path, use_incremental=incremental) | |
| target_path = _validate_path(target) if target else Path.cwd() | |
| project_root = target_path if target_path.is_dir() else target_path.parent | |
| with console.status("[primary]Executing pipeline run...[/primary]"): | |
| try: | |
| pipeline = RefactronPipeline(project_root=project_root) | |
| result = pipeline.analyze(target_path, use_incremental=incremental) |
🧰 Tools
🪛 GitHub Actions: Pre-commit
[error] pre-commit hook "black" failed (files were reformatted by the hook)
[error] pre-commit hook "isort" failed (files were modified/fixed by the hook)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@refactron/cli/run.py` around lines 28 - 33, RefactronPipeline is being
instantiated without the target project's path so config discovery defaults to
the current working directory; change the code that creates RefactronPipeline()
to pass the effective project root derived from target_path (from
_validate_path) — e.g. construct RefactronPipeline with the project root
argument (or call its load/config initializer) so that
pipeline.analyze(target_path, use_incremental=incremental) picks up
/other/project/.refactron.yaml instead of the CWD.
| console.print(f"[green]Pipeline analysis completed successfully.[/green]") | ||
| console.print(f"Total files analyzed: {result.total_files}") | ||
| console.print(f"Total issues found: {result.total_issues}") | ||
|
|
||
| except Exception as e: | ||
| console.print(f"[red]Pipeline run failed: {e}[/red]") | ||
| raise SystemExit(1) |
There was a problem hiding this comment.
Fix the lint failure and preserve exception chaining.
Line 35 is an f-string without placeholders, and the SystemExit raised from the except block should be chained or explicitly suppressed.
Proposed fix
- console.print(f"[green]Pipeline analysis completed successfully.[/green]")
+ console.print("[green]Pipeline analysis completed successfully.[/green]")
console.print(f"Total files analyzed: {result.total_files}")
console.print(f"Total issues found: {result.total_issues}")
- except Exception as e:
- console.print(f"[red]Pipeline run failed: {e}[/red]")
- raise SystemExit(1)
+ except Exception as exc:
+ console.print(f"[red]Pipeline run failed: {exc}[/red]")
+ raise SystemExit(1) from excAs per coding guidelines, **/*.py: “Use line length of 100 characters, enforced by black, isort, and flake8”.
🧰 Tools
🪛 GitHub Actions: Pre-commit
[error] 38-38: flake8 reported F541 f-string is missing placeholders
[error] pre-commit hook "black" failed (files were reformatted by the hook)
[error] pre-commit hook "isort" failed (files were modified/fixed by the hook)
🪛 Ruff (0.15.10)
[error] 35-35: f-string without any placeholders
Remove extraneous f prefix
(F541)
[warning] 39-39: Do not catch blind exception: Exception
(BLE001)
[warning] 41-41: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@refactron/cli/run.py` around lines 35 - 41, The first console.print uses an
f-string with no placeholders (console.print(f"[green]Pipeline analysis
completed successfully.[/green]"))—replace it with a normal string literal to
satisfy linting (console.print("[green]Pipeline analysis completed
successfully.[/green]")), and in the except block preserve exception chaining by
re-raising SystemExit with the caught exception (use raise SystemExit(1) from e)
so the original traceback (e) is kept; ensure lines remain under the
100-character limit.
| from pathlib import Path | ||
| from typing import Optional, Union, List | ||
|
|
||
| from refactron.core.config import RefactronConfig | ||
| from refactron.core.refactron import Refactron | ||
| from refactron.core.analysis_result import AnalysisResult | ||
| from refactron.core.models import CodeIssue | ||
| from refactron.autofix.engine import AutoFixEngine | ||
|
|
||
| from refactron.core.config import RefactronConfig | ||
| from refactron.core.refactron import Refactron | ||
| from refactron.core.analysis_result import AnalysisResult | ||
|
|
||
| class RefactronPipeline: | ||
| """Automated pipeline execution for session-based and CI/CD flows.""" | ||
|
|
||
| def __init__(self, project_root: Optional[Union[str, Path]] = None, config_path: Optional[Union[str, Path]] = None): |
There was a problem hiding this comment.
Fix the duplicate imports and formatter failures.
RefactronConfig, Refactron, and AnalysisResult are imported twice, and Line 19 exceeds the 100-character formatter/lint limit. This is already causing black/isort pre-commit failures.
Proposed cleanup
from pathlib import Path
-from typing import Optional, Union, List
+from typing import Dict, List, Optional, Union
+from refactron.autofix.engine import AutoFixEngine
+from refactron.core.analysis_result import AnalysisResult
from refactron.core.config import RefactronConfig
-from refactron.core.refactron import Refactron
-from refactron.core.analysis_result import AnalysisResult
from refactron.core.models import CodeIssue
-from refactron.autofix.engine import AutoFixEngine
-
-from refactron.core.config import RefactronConfig
from refactron.core.refactron import Refactron
-from refactron.core.analysis_result import AnalysisResult
+
class RefactronPipeline:
"""Automated pipeline execution for session-based and CI/CD flows."""
- def __init__(self, project_root: Optional[Union[str, Path]] = None, config_path: Optional[Union[str, Path]] = None):
+ def __init__(
+ self,
+ project_root: Optional[Union[str, Path]] = None,
+ config_path: Optional[Union[str, Path]] = None,
+ ):As per coding guidelines, **/*.py: “Use line length of 100 characters, enforced by black, isort, and flake8”.
🧰 Tools
🪛 GitHub Actions: Pre-commit
[error] pre-commit hook "black" failed (files were reformatted by the hook)
[error] pre-commit hook "isort" failed (files were modified/fixed by the hook)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@refactron/core/pipeline.py` around lines 3 - 19, Remove the duplicated
imports of RefactronConfig, Refactron, and AnalysisResult (keep a single import
line for each unique symbol) and update the RefactronPipeline.__init__ signature
so it does not exceed the 100-character line limit (wrap parameters across
multiple lines or use type aliases for Optional[Union[str, Path]]/config_path)
to satisfy black/isort/flake8 formatting rules.
| self._config.enable_incremental_analysis = False | ||
| self.refactron = Refactron(self._config) | ||
| self.autofix_engine = AutoFixEngine() | ||
| self._fixer_cache = {} |
There was a problem hiding this comment.
Annotate _fixer_cache to unblock mypy.
The untyped empty dict makes reads return Any, which is why mypy reports both the missing annotation and the Optional[str] return mismatch.
Proposed fix
- self._fixer_cache = {}
+ self._fixer_cache: Dict[str, Optional[str]] = {}As per coding guidelines, refactron/**/*.py: “Type annotations are required in refactron/ with mypy disallow_untyped_defs = true enabled”.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| self._fixer_cache = {} | |
| self._fixer_cache: Dict[str, Optional[str]] = {} |
🧰 Tools
🪛 GitHub Actions: Pre-commit
[error] pre-commit hook "black" failed (files were reformatted by the hook)
[error] pre-commit hook "isort" failed (files were modified/fixed by the hook)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@refactron/core/pipeline.py` at line 35, The attribute _fixer_cache is untyped
which yields Any; annotate it as a mapping of fixer keys to optional strings
(e.g. Dict[str, Optional[str]]) to satisfy mypy and the Optional[str] return
expectations: change the assignment in the class from self._fixer_cache = {} to
self._fixer_cache: Dict[str, Optional[str]] = {} and add the necessary typing
imports (from typing import Dict, Optional) at the module top if not present.
| if use_incremental is not None: | ||
| self.refactron.config.enable_incremental_analysis = use_incremental | ||
|
|
There was a problem hiding this comment.
Apply incremental overrides to the initialized tracker, not only the config.
Refactron constructs IncrementalAnalysisTracker(enabled=self.config.enable_incremental_analysis) during initialization, so changing only self.refactron.config.enable_incremental_analysis here can leave the actual tracker running with the old mode. Rebuild Refactron or update the tracker state when use_incremental changes.
Possible fix
if use_incremental is not None:
- self.refactron.config.enable_incremental_analysis = use_incremental
+ self._config.enable_incremental_analysis = use_incremental
+ self.refactron.config.enable_incremental_analysis = use_incremental
+ self.refactron.incremental_tracker.enabled = use_incremental🧰 Tools
🪛 GitHub Actions: Pre-commit
[error] pre-commit hook "black" failed (files were reformatted by the hook)
[error] pre-commit hook "isort" failed (files were modified/fixed by the hook)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@refactron/core/pipeline.py` around lines 62 - 64, The code updates only
self.refactron.config.enable_incremental_analysis when use_incremental is
provided but leaves the live tracker unchanged; update the actual
IncrementalAnalysisTracker on the Refactron instance as well. After setting
self.refactron.config.enable_incremental_analysis, either set the existing
tracker’s enabled state (e.g. self.refactron.incremental_tracker.enabled =
use_incremental or call its setter method) or recreate it:
self.refactron.incremental_tracker =
IncrementalAnalysisTracker(enabled=use_incremental) (import
IncrementalAnalysisTracker if needed) so the running tracker matches the new
config.
| # Use rule_id or category as cache key | ||
| issue_type = issue.rule_id or str(getattr(issue, 'category', type(issue).__name__)) | ||
| if issue_type in self._fixer_cache: | ||
| return self._fixer_cache[issue_type] | ||
|
|
||
| candidate_name = None | ||
|
|
||
| # 1. Ask AutoFixEngine directly without preview (O(1) dictionary lookup) | ||
| if hasattr(self, 'autofix_engine') and self.autofix_engine.can_fix(issue): | ||
| candidate_name = issue.rule_id | ||
| else: | ||
| # 2. Fallback to preview-based resolution for ambiguous cases | ||
| if hasattr(self, 'autofix_engine'): | ||
| for name, fixer in self.autofix_engine.fixers.items(): | ||
| try: | ||
| preview_result = fixer.preview(issue, "x = 1\n") | ||
| if preview_result and getattr(preview_result, "success", False): | ||
| candidate_name = name | ||
| break | ||
| except Exception: | ||
| continue | ||
|
|
||
| self._fixer_cache[issue_type] = candidate_name |
There was a problem hiding this comment.
Avoid caching fallback matches by broad category only.
When rule_id is missing, the cache key collapses to the issue category, so one successful preview for a STYLE issue can assign the same fixer to unrelated STYLE issues without re-evaluating them. Either skip caching for rule-less issues or include more issue-specific fields in the fallback key.
Safer cache key option
- issue_type = issue.rule_id or str(getattr(issue, 'category', type(issue).__name__))
+ if issue.rule_id:
+ issue_type = issue.rule_id
+ else:
+ issue_type = ":".join(
+ [
+ str(getattr(issue, "category", type(issue).__name__)),
+ str(getattr(issue, "message", "")),
+ ]
+ )🧰 Tools
🪛 GitHub Actions: Pre-commit
[error] 84-84: mypy error: Returning Any from function declared to return "Optional[str]" [no-any-return]
[error] pre-commit hook "black" failed (files were reformatted by the hook)
[error] pre-commit hook "isort" failed (files were modified/fixed by the hook)
🪛 Ruff (0.15.10)
[error] 97-98: try-except-continue detected, consider logging the exception
(S112)
[warning] 97-97: Do not catch blind exception: Exception
(BLE001)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@refactron/core/pipeline.py` around lines 78 - 100, The current caching uses
issue_type = issue.rule_id or str(getattr(issue, 'category', ...)) which causes
fallback preview matches to be cached broadly; change the logic in the fixer
resolution in resolve/fixer lookup so that when issue.rule_id is missing you
either (a) do not write the fallback candidate_name into self._fixer_cache for
that issue_type, or (b) make the cache key more specific by including additional
issue-specific fields (e.g., a unique identifier, file/line, or a short snippet
from issue.source/statement) before assigning self._fixer_cache[issue_type] =
candidate_name; ensure this applies only when using the preview-based loop over
self.autofix_engine.fixers and keep the fast-path autofix_engine.can_fix(issue)
caching unchanged.
| # Should only have run preview 1 time, because the subsequent issues have the same rule_id and hit the cache. | ||
| assert calls["preview"] == 1 |
There was a problem hiding this comment.
Wrap the long comment to satisfy flake8.
Line 105 exceeds the configured 100-character limit and is already failing pre-commit.
Proposed fix
- # Should only have run preview 1 time, because the subsequent issues have the same rule_id and hit the cache.
+ # Should only have run preview 1 time because subsequent issues have the
+ # same rule_id and hit the cache.As per coding guidelines, **/*.py: “Flake8 linting must use max-line-length of 100”.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Should only have run preview 1 time, because the subsequent issues have the same rule_id and hit the cache. | |
| assert calls["preview"] == 1 | |
| # Should only have run preview 1 time because subsequent issues have the | |
| # same rule_id and hit the cache. | |
| assert calls["preview"] == 1 |
🧰 Tools
🪛 GitHub Actions: Pre-commit
[error] pre-commit hook "black" failed (files were reformatted by the hook)
[error] pre-commit hook "isort" failed (files were modified/fixed by the hook)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/test_pipeline.py` around lines 105 - 106, The inline comment above the
assertion in tests/test_pipeline.py is longer than 100 characters; split or
reflow that comment into multiple shorter lines (each <=100 chars) so it
complies with flake8 max-line-length. Locate the long comment that precedes the
assertion using the assertion snippet calls["preview"] == 1 (or the surrounding
test function in test_pipeline.py) and break the sentence into two or more
shorter comment lines or rephrase it to keep each line under the 100-character
limit.
45328d1 to
1acc555
Compare
…zed fixer resolution
1acc555 to
a27612d
Compare
|
This pull request has been automatically marked as stale because it has not had recent activity for 14 days. If you believe this PR is still relevant, please:
This helps us keep the repository focused on active contributions. Thank you! 🙏 |
|
This pull request has been automatically closed because it was marked as stale and had no recent activity for 3 days. If you believe this PR should be reopened, please:
Thank you for your understanding! 🙏 |
solve #176
This update resolves a significant performance bottleneck in the automated refactoring pipeline caused by the expensive preview-based fixer resolution. We replaced the highly unoptimized workflow, which repetitively executed a simulated code change against dummy code via the AST for every single detected issue. To remediate this, we introduced an O(1) direct mapping mechanism utilizing the AutoFixEngine's registry can_fix check—enabling the orchestrator to assign rule-specific issues unambiguously straight to candidates. For unresolved subsets requiring arbitrary match evaluation, the robust preview-generation fallback remains. The entire operation is now heavily optimized through a new localized _fixer_cache within the RefactronPipeline, caching evaluations down to their granular categories or rule_ids, radically reducing overhead across the matrix of issue lists without triggering regression.
Summary by CodeRabbit
Release Notes
runcommand for executing pipeline analysis on target directories