fix: lint gate returns SKIPPED when linter binary is missing (#372) - #378
Conversation
…ies (#372) When `uv run ruff` fails because ruff isn't a project dependency, the gate now returns GateStatus.SKIPPED instead of GateStatus.FAILED. This prevents the ReactAgent from wasting iterations trying to fix phantom lint errors that don't actually exist. Detects tool-not-found via stderr patterns: "Failed to spawn", "command not found", "No such file or directory".
WalkthroughThe per-file lint gate now detects tool-not-found failures (e.g., "Failed to spawn", "No such file or directory") after running the linter and returns GateStatus.SKIPPED with an explanatory message and duration. A test was added for the Changes
Sequence Diagram(s)(omitted) Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Comment |
Return SKIPPED from
|
Code Review SummaryThis PR successfully addresses issue #372 by improving the lint gate's error detection to distinguish between "linter not found" and "actual lint errors". The implementation is clean, well-tested, and follows the repository's patterns. ✅ Strengths1. Correct Problem DiagnosisThe fix addresses the root cause: when 2. Clean Implementation (codeframe/core/gates.py:678-691)
3. Comprehensive Test Coverage (test_gates_observability.py:259-279)
4. Documentation UpdatedDocstring now clearly explains all SKIPPED scenarios including the new tool-not-found case. 🔍 ObservationsCode Quality
Performance
Security
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@codeframe/core/gates.py`:
- Around line 678-691: The SKIPPED detection is too broad because it treats any
"no such file or directory" as the tool being missing; update the check in the
error-handling block that builds _NOT_FOUND so that "no such file or directory"
only triggers SKIPPED when the tool name (cfg.name) or the binary basename
appears in stderr_lower (or when stderr contains a spawn-related phrase), e.g.,
keep "failed to spawn" and "command not found" as global matches but require
that "no such file or directory" is accompanied by cfg.name or the executable
name found in result.args; return GateCheck(GateStatus.SKIPPED, ...) only when
one of these tightened conditions is met (use stderr_lower, cfg.name, and
result.args to locate the binary).
…g target files Address PR review feedback: "no such file or directory" now only triggers SKIPPED when the tool name also appears in stderr, preventing false positives when the linter reports a missing *target* file rather than a missing *binary*. Simplify redundant test assertion.
Code Review - Second Review ✅I've reviewed the latest changes (commit 225f3f0) that address the previous feedback. The improvements look excellent! ✅ Improvements Implemented1. Narrowed Tool-Not-Found Detection (gates.py:686-690)The fix for the false positive concern is well-implemented: or ("no such file or directory" in stderr_lower and tool_name in stderr_lower)Why this is good:
Example scenarios covered:
2. Simplified Test Assertion (test_gates_observability.py:279)The redundant assertion was removed: # Before: assert "not found" in check.output.lower() or "not found" in (check.output or "").lower()
# After: assert "not found" in check.output.lower()This is cleaner and sufficient. 🔍 Code Quality AssessmentImplementation CorrectnessLocation: codeframe/core/gates.py:678-697 Strengths:
Edge Cases Considered✅ Empty stderr: Handled by Potential False NegativesThe current implementation might miss some tool-not-found scenarios:
However, these are acceptable trade-offs because:
🧪 Test CoverageTest Quality (test_gates_observability.py:259-279)Strengths:
Coverage Analysis:
Missing test cases (optional, not blocking):
These aren't critical since the main regression scenario is well-covered. 📊 Performance & SecurityPerformance Impact
Security Considerations
🎯 Impact on ReactAgentBefore Fix# Every file creation → GateStatus.FAILED
# Agent sees: "Lint errors detected" (phantom)
# Result: Wastes all 30 iterations trying to fixAfter Fix# File creation → GateStatus.SKIPPED
# Agent sees: Empty string (line 637-640 in react_agent.py)
# Result: Continues with next stepVerified: The existing ReactAgent code (react_agent.py:637-640) correctly handles SKIPPED by returning empty string, so this fix requires no changes to the agent logic. 📋 Repository Standards CompliancePer CLAUDE.md guidelines:
🚀 Recommendation✅ APPROVE - Ready to Merge This PR successfully resolves issue #372 with:
The latest commit (225f3f0) effectively addresses the CodeRabbit feedback about false positives. The implementation is production-ready. 📝 Minor Suggestions for Future (Not Blocking)If you want to further improve this area in the future:
But these are nice-to-haves, not requirements. Great work addressing the feedback! The narrowed detection logic is exactly what was needed. 🎉 |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@codeframe/core/gates.py`:
- Around line 678-691: The current "no such file or directory" check only looks
for cfg.cmd[0] (tool_name) which misses wrapper invocations (e.g., npx eslint);
update the matching to consider both the wrapper and the real tool by building a
set of candidate names from cfg.cmd (e.g., cfg.cmd[0] plus cfg.cmd[1] if
present, or any parsed subcommand) and check if any candidate appears in
stderr_lower before treating the error as a tool-not-found; adjust the condition
around result.returncode/result.stderr and the existing variables (stderr_lower,
tool_name) to use this multi-name membership test.
Address CodeRabbit review feedback: when using wrapper commands like `npx eslint`, cfg.cmd[0] is the wrapper, not the actual tool. Use both cfg.cmd[0] and cfg.name to detect "no such file or directory" errors.
Code Review - Third ReviewI've reviewed the latest commit (2a4c52d) which addresses CodeRabbit's feedback about wrapper invocations. The implementation continues to improve! Latest Improvement: Multi-Name MatchingThe detection now checks both cfg.cmd[0] and cfg.name when matching tool-not-found errors (gates.py:686). This handles wrapper commands like npx where cfg.cmd[0]=npx but cfg.name=eslint. If stderr mentions eslint, the code now catches it. Code Quality AssessmentImplementation strengths:
Edge cases now covered:
The approach is sufficient because cfg.cmd[0] (executable) and cfg.name (tool) cover the two names that appear in error messages. Checking cfg.cmd[1] (args like check) would be overkill. Final RecommendationAPPROVE - Production Ready This PR evolved through three iterations:
The implementation is:
Ready to merge! |
Summary
run_lint_on_file()now returnsGateStatus.SKIPPED(notFAILED) when the linter binary isn't installed in the target project's dependenciesuv run: "Failed to spawn", "command not found", "No such file or directory"Changes
codeframe/core/gates.py: Added tool-not-found detection between subprocess result capture and pass/fail decisiontests/core/test_gates_observability.py: Addedtest_uv_run_missing_tool_returns_skippedcovering theuv runfailure scenarioTest Plan
test_uv_run_missing_tool_returns_skipped— mocksuv run ruffreturning exit code 2 with "Failed to spawn" stderr, verifies SKIPPEDtest_missing_linter_binary_skips— still passes (covers binary not on PATH at all)ruff check)Closes #372
Summary by CodeRabbit