fix(verify): classify environment faults per shell so win32 stops burning attempts (#302)#308
Conversation
|
Warning Review limit reached
Next review available in: 46 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
WalkthroughVerify environment-fault detection now distinguishes POSIX and Windows shell behavior. Windows classification uses command output, return codes, and executable resolution, with expanded engine and unit-test coverage plus updated documentation. ChangesVerify environment fault handling
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant VerifyCommandsOutcome
participant EnvFaultReason
participant CmdExe
participant Engine
VerifyCommandsOutcome->>EnvFaultReason: classify verify result
EnvFaultReason->>CmdExe: probe command token
CmdExe-->>EnvFaultReason: return code and command resolution
EnvFaultReason-->>VerifyCommandsOutcome: fault reason or no fault
VerifyCommandsOutcome->>Engine: escalate environment fault
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ 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.
🧹 Nitpick comments (1)
tests/test_verify.py (1)
427-445: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winThese "ordinary failure" cases silently depend on
pytestbeing resolvable viashutil.which.Every
pytest …row reaches the token probe (rc≠0, no cmd message) and only returnsNonebecauseshutil.which("pytest")succeeds. Under apython -m pytestinvocation from an env whoseScripts\dir is not onPATH,whichreturnsNoneand the classifier reportspytest not found on PATH, failing the assertion for reasons unrelated to the behavior under test. Pinning resolution makes the matrix hermetic:♻️ Make token resolution explicit
def test_win32_ordinary_failures_are_not_env_faults(tmp_path, command, rc, tail): + # the probe's PATH lookup is not the subject here: pin it so the matrix + # asserts classification, not this interpreter's PATH layout. + monkeypatch.setattr(verify.shutil, "which", lambda name: rf"C:\tools\{name}.exe") assert verify.env_fault_reason(verify.CommandResult(command, rc, tail), tmp_path) is None(add
monkeypatchto the signature). Note the same dependency applies toruff checkrows.🤖 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/test_verify.py` around lines 427 - 445, Make test_win32_ordinary_failures_are_not_env_faults independent of the host PATH by adding monkeypatch to its signature and explicitly patching the token-resolution lookup used by verify.env_fault_reason so pytest and ruff resolve successfully. Preserve the existing command matrix and assertions while ensuring the cmd builtin, malformed, and other non-tool cases retain their current behavior.
🤖 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.
Nitpick comments:
In `@tests/test_verify.py`:
- Around line 427-445: Make test_win32_ordinary_failures_are_not_env_faults
independent of the host PATH by adding monkeypatch to its signature and
explicitly patching the token-resolution lookup used by verify.env_fault_reason
so pytest and ruff resolve successfully. Preserve the existing command matrix
and assertions while ensuring the cmd builtin, malformed, and other non-tool
cases retain their current behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: c1c30c33-a1bc-4f55-9caf-f86beee339fd
📒 Files selected for processing (6)
CHANGELOG.mddocs/FEATURES.mdsrc/bmad_loop/verify.pytests/conftest.pytests/test_engine.pytests/test_verify.py
1dfdbff to
06b7c4f
Compare
|
@coderabbitai The nitpick is valid and is now addressed in Confirmed the dependency: every I took the fix with one deliberate change from the suggested diff. Resolving every name: monkeypatch.setattr(verify.shutil, "which", lambda name: rf"C:\tools\{name}.exe")would make the matrix hermetic but strip several rows of their evidence: So the fake pins a host that has exactly the two tools the matrix names, and nothing else: monkeypatch.setattr(
verify.shutil,
"which",
lambda name: rf"C:\tools\{name}.exe" if name in {"pytest", "ruff"} else None,
)Hermetic, and every row stays load-bearing. Two related rows do not share the dependency, so they were left alone: Verified after the change: Windows 11 |
|
Tip For best results, initiate chat on the files or code changes.
If you found this review helpful, would you consider giving us a shout-out on X? Thank you for using CodeRabbit! |
06b7c4f to
74b210d
Compare
|
Reviewed and rebased onto Rebase: one conflict that needed a decision, not a merge
Kept Removed the
|
Greptile SummaryThis PR fixes a Windows-specific regression where verify commands that fail due to a broken environment (missing tool, unrunnable file) were never classified as environment faults — burning dev-session budget on repairs that can't succeed. The fix replaces the single
Confidence Score: 5/5Safe to merge — the change is additive on POSIX and the win32 arm is guarded by four independently-verified false-positive checks that are each unit-tested. The classifier logic is tightly scoped to a single public function that replaces a one-liner rc-set check. Each win32 signal and each guard has a dedicated unit test, including all edge cases enumerated in the PR description. The three engine-level integration tests now run on both platforms. No production path is altered on POSIX. Files Needing Attention: No files require special attention. The most complex logic is in _win32_env_fault_reason in verify.py, and its decision tree is fully exercised by the new test matrix.
|
| Filename | Overview |
|---|---|
| src/bmad_loop/verify.py | Core classifier refactor: adds per-shell env_fault_reason with four guards against false positives; logic is well-documented and matches the test matrix exactly |
| tests/test_verify.py | Comprehensive unit tests for every signal and guard in the win32 arm; win32 rows drive env_fault_reason with synthetic CommandResults |
| tests/conftest.py | Adds cross-platform verify-script helpers and MISSING_TOOL_CMD constant; centralises OS branching in one place |
| tests/test_engine.py | Removes three skipif(win32) guards, replaces POSIX-only shell verbs with cross-platform helpers |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[env_fault_reason called] --> B{rc in 126 / 127?}
B -->|yes| C[return rc=N POSIX env fault]
B -->|no| D{sys.platform == win32?}
D -->|no| E[return None]
D -->|yes| F[_win32_env_fault_reason]
F --> G{rc less than 0?}
G -->|yes| H[return None timeout]
G -->|no| I{rc == 9009?}
I -->|yes| J[return rc=9009]
I -->|no| K{rc != 0 AND tail non-empty?}
K -->|yes| L{is not recognized in last 2 lines?}
L -->|yes| M[return cmd message]
L -->|no| N{access is denied in last line?}
N -->|yes| O[return cmd message]
K -->|no| P[token probe]
N -->|no| P
P --> Q{_leading_token is None?}
Q -->|yes| R[return None]
Q -->|no| S{token names a file in cwd?}
S -->|yes| T{not executable by PATHEXT AND not builtin?}
T -->|yes| U[return not executable by cmd]
T -->|no| V[return None]
S -->|no| W{rc != 0 AND not local_executable AND not builtin AND which is None?}
W -->|yes| X[return not found on PATH]
W -->|no| Y[return None]
Reviews (2): Last reviewed commit: "fix(verify): classify verify env faults ..." | Re-trigger Greptile
ENV_FAULT_RCS = {126, 127} is sh's launcher convention, but verify commands run
through the host shell, and cmd has no equivalent. Measured on Windows 11:
cmd /c <missing tool> -> exit 1 ("… is not recognized …")
probe.cmd calling the same -> ERRORLEVEL 9009, only inside the batch file
cmd /c <path to .sh/.txt> -> exit 0 handed to the file association, never run
So the env-fault arm never fired on win32. A broken verify environment either
burned dev attempts on repairs no session can make, or — worse — reported the
check as passed when nothing had executed.
The classifier is now per shell. POSIX keeps rc 126/127 verbatim, and the rc arm
is checked ahead of the per-shell branch, so it still fires on both. The win32
arm takes three independent signals: rc 9009 (a .cmd/.bat wrapper propagating
%ERRORLEVEL%), cmd's own "is not recognized" / "access is denied" closing the
command's output, and a resolvability probe of the leading token. The probe's
file branch also fires at rc 0, closing the silent false pass.
Four guards keep the win32 arm from escalating a healthy run:
- a token cmd expands (%VAR%, !VAR!) is unprobeable and skips the probe
- executability of an existing file is decided by PATHEXT, not shutil.which,
which resolves a relative path against the process cwd rather than the run's
- cmd searches the run's own directory before PATH, so the not-found-on-PATH
branch is skipped when the token names a file there
- a timed-out command (rc -1) ran and hung, so it is never classified
Tests: the three engine tests that carried skipif(win32) for exactly this gap
now run on both platforms, and the classifier's matrix is unit-tested. Three
tests carried POSIX-only verify commands (true / false / a bare .sh) that cmd
cannot run; one of them was already failing on win32, the other two passed for
the wrong reason. They now use the host-shell verbs, named in conftest's single
platform-detection spot as _OK / _FAIL so the next test does not reach for
true/false again.
Closes bmad-code-org#302
74b210d to
ed77c6b
Compare
Closes #302
Problem
ENV_FAULT_RCS = frozenset({126, 127})is sh's launcher convention. Verify commands runshell=True, so on Windows the launcher iscmd— and cmd has no equivalent. Measured on Windows 11 (the issue's premise was corrected in a comment):So 9009 never reaches the orchestrator unless the verify command is itself a
.cmd/.batthat propagates it, and the common "pytestis not on PATH" fault arrives as rc 1 — the same code an ordinary test failure uses. The env-fault arm was therefore unreachable on win32: a broken environment burned dev attempts on repairs no session can make (the regression #130 fixed on POSIX), and the three engine tests that pin the behavior wereskipif(win32), so nothing caught it.The
rc 0line above is the second half: a verify command naming a file cmd cannot execute reports passed for a check that never ran.Fix
The exit-code test becomes a per-shell classifier (
verify.env_fault_reason). POSIX is unchanged. The win32 arm takes three independent signals:is not recognized/access is denied, matched against the closing lines of the output (cmd's message wraps onto two), so a subprocess that merely prints the phrase mid-run stays an ordinary failure.rc 0, closing the silent false pass.Four guards keep the arm from escalating a healthy run — each one a false positive found while building it:
%VAR%,!VAR!, `&PATHEXT(appended, as cmd does), notshutil.whichwhichresolves a relative path against the process cwd, not the run's — under worktree isolation a green run escalatescheck.cmdfailing there → env fault instead of a fix session-1) is never classifiedParens and a leading
@are stripped from both ends ((pytest)tokenized topytest)), andis_file()is wrapped forOSError.Tests
skipif(sys.platform == "win32")engine tests now run on both platforms; their assertions moved offrc=126onto the classification itself.conftestgains the host-shell env-fault builders (MISSING_TOOL_CMD,_self_disarming_cmd,_write_check_script/_disarm_check_script) in the file's existing single platform-detection spot.env_fault_reasonwith synthetic results rather than executing an unrunnable path — doing that for real pops the file-association picker mid-suite (Windows: env-fault engine test replays check.sh through cmd — pops the 'How do you want to open this file?' dialog on every local full-suite run #292)._self_disarming_cmdburns a flag file and reaches for a missing tool on the second run.true,false, a bare.sh) that cmd cannot run.test_verify_review_happy_and_commandswas already failing on win32 before this change; the other two passed for the wrong reason — a missing command reading as an ordinary failure. All three now use host-shell verbs.Verification
pytest tests/test_engine.py tests/test_verify.py→ 395 passed, 5 skipped (the skips are the POSIX-only classifier test + upstream's POSIX symlink tests)ruff check+ruff format --checkclean;prettier@3.8.4clean on the touched markdown; pyright reports nothing new inverify.pyScope
plugins/bus.pyis the othershell=Truesite but has no env-fault classification at all, so it is deliberately untouched. Rebased ona82be01.Summary by CodeRabbit