Skip to content

fix(skillopt-sleep): surface Claude CLI spawn failures instead of silent zero scores - #126

Merged
Yifan Yang (Yif-Yang) merged 2 commits into
microsoft:mainfrom
Phoenix0531-sudo:fix/surface-claude-cli-errors
Jul 12, 2026
Merged

Yifan Yang (Yif-Yang) merged 2 commits into
microsoft:mainfrom
Phoenix0531-sudo:fix/surface-claude-cli-errors

Conversation

@Phoenix0531-sudo

@Phoenix0531-sudo ClumsyLucid (Phoenix0531-sudo) commented Jul 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Make ClaudeCliBackend report subprocess spawn failures (e.g. FileNotFoundError on Windows) instead of silently swallowing them and returning an empty string that scores 0.0 everywhere.

Motivation

Two stacked problems (detailed in #121):

  1. Path resolution. On Windows, claude installed via npm is a .cmd shim. subprocess.run(["claude", ...]) raises FileNotFoundError because CreateProcess doesn't resolve .cmd by bare name. (PR fix: make ClaudeCliBackend work on Windows (.cmd shim resolution + argv length) #98 addresses this with shutil.which().)

  2. Silent swallow. Regardless of why the CLI fails to spawn, both _call and attempt_with_tools wrap the subprocess call in a bare except Exception: return "" that never sets last_call_error or logs a warning. The empty string is then treated as a legitimate model answer, so the run proceeds and all scores come back 0.0 — indistinguishable from "nothing to optimize".

This PR fixes problem 2, following the same pattern established in #92 for the codex backend.

Changes

Engine

  • _call (line 623): Catch the exception explicitly, set last_call_error, log a warning, then return "". Control flow preserved.
  • attempt_with_tools (line 687): Same treatment.

Both ensure that a dead or broken CLI can never masquerade as a 0.0-scoring model.

Tests

  • test_spawn_failure_sets_last_call_error (new): Verifies _call sets last_call_error and returns "" when subprocess.run raises FileNotFoundError.
  • test_attempt_tools_spawn_failure_sets_last_call_error (new): Same for attempt_with_tools.

Testing

uv run python -m pytest tests/test_sleep_engine.py::TestClaudeCliBackendBare -v --tb=short

All 5 tests pass (3 pre-existing + 2 new).

Full suite:

uv run python -m pytest tests/test_sleep_engine.py -v --tb=short
  • 62 passed, 1 skipped, 3 failed
  • The 3 failures are pre-existing Windows path separator issues (\ vs /), completely unrelated to this change.

Related issue

Closes #121

…ent zero scores

In _call and attempt_with_tools, the bare 'except Exception: return ""'
swallows FileNotFoundError (e.g. bare 'claude' on Windows with npm .cmd
shim) and any other spawn failure, returning an empty string that the
trainer treats as a legitimate model response that scores 0.0 everywhere.

Now the exception is caught explicitly: last_call_error is set, a
warning is logged, and the empty-string return is preserved for
backward compatibility of the control flow. This mirrors the pattern
from microsoft#92 (codex backend) which fixed the same class of 'dead CLI
masquerades as nothing to learn' bug.

Issue: microsoft#121
…re surfaced

Add two tests to TestClaudeCliBackendBare:
- test_spawn_failure_sets_last_call_error: _call sets last_call_error
  and returns '' when subprocess.run raises FileNotFoundError.
- test_attempt_tools_spawn_failure_sets_last_call_error: same for
  attempt_with_tools.

These prove the fix from the parent commit (surface spawn failures
instead of silently scoring 0) and guard against regressions.
@Phoenix0531-sudo

Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree

@Yif-Yang
Yifan Yang (Yif-Yang) merged commit 7df4965 into microsoft:main Jul 12, 2026
1 check passed
@Phoenix0531-sudo
ClumsyLucid (Phoenix0531-sudo) deleted the fix/surface-claude-cli-errors branch July 13, 2026 01:48
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.

ClaudeCliBackend on Windows: bare "claude" not resolvable (npm .cmd shim), FileNotFoundError swallowed, whole run silently scores 0.0

2 participants