Skip to content

Windows: verify env-fault classification never fires — cmd reports missing/non-executable tools as 9009/1, not 126/127 #302

Description

@dracic

Summary

ENV_FAULT_RCS = frozenset({126, 127}) (src/bmad_loop/verify.py:1376) encodes the sh launcher convention: 126 = found but not executable, 127 = not found. run_verify_commands runs commands with shell=True, so on Windows the shell is cmd, which never uses those codes — a missing executable is 9009 ("is not recognized as an internal or external command") and a non-executable one typically surfaces as 1.

The consequence is that on Windows the env-fault arm at verify.py:1410 is unreachable in practice. A broken verify environment is classified an ordinary fixable failure, so the run dispatches repair sessions and burns dev attempts against a tree no session can fix — precisely the failure #126 was filed for and #130 fixed on POSIX.

The comment at verify.py:1374-1375 states the gap ("Windows cmd signals these as 9009/1 instead, so it keeps the charged behavior") but nothing tracks closing it.

Evidence

The three engine tests that pin this behavior are all win32-skipped for exactly this reason:

  • test_review_verify_env_fault_escalates_instead_of_fix_session
  • test_skip_review_env_fault_escalates_not_defers
  • test_fix_phase_env_fault_escalates_instead_of_looping

each carrying @pytest.mark.skipif(sys.platform == "win32", reason="sh env-fault exit codes (cmd reports 9009)"). So the Windows behavior is neither correct nor exercised.

Why this is not a trivial patch

Adding 9009 is safe and unambiguous. Adding 1 is not — under cmd, rc 1 is also the ordinary "the tests failed" code, which is exactly the fixable failure that should route to a repair session. Classifying it an env fault would pause runs on every genuine test failure on Windows.

So the fix needs a decision on how far to go, roughly:

  1. 9009 only — narrow, no false positives, catches the common "tool not on PATH" case, leaves non-executable files charged.
  2. 9009 + a stderr probe — treat rc 1 as an env fault only when the output tail matches cmd's not-recognized/access-denied phrasing. Catches more, but pattern-matches localized shell text.
  3. Bypass the shell for env detection — resolve the command's leading token before running it and classify unresolvable ones without relying on the shell's exit code at all.

Whatever is chosen, ENV_FAULT_RCS stops being a platform-neutral frozenset and becomes a per-shell classifier; the three skips above should be lifted as part of the change so the Windows path is actually pinned.

Refs

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions