fix(sleep): make --bare conditional on ANTHROPIC_API_KEY (#68)

ClaudeCliBackend._call() and attempt_with_tools() hardcoded --bare,
which skips Claude CLI's credential resolution. This broke subscription-
token auth: every model call silently returned "Not logged in" and
scored 0 — the user saw "baseline 0.0 → candidate 0.0, gate reject"
with no indication of an auth failure.

Fix: only pass --bare when ANTHROPIC_API_KEY is set. The remaining
isolation flags (--disable-slash-commands, --disallowedTools,
--exclude-dynamic-system-prompt-sections, clean temp cwd) already
provide the needed isolation without --bare.

Also adds _detect_cli_error() to log a warning when CLI output matches
known auth error patterns, so auth failures surface loudly instead of
deflating every score to 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
carpedkm
2026-06-20 13:28:34 +00:00
parent 24b5a25ba8
commit bfa53bc46d
2 changed files with 102 additions and 7 deletions
+62
View File
@@ -944,5 +944,67 @@ class TestCopilotBackend(unittest.TestCase):
shutil.rmtree(stub_dir, ignore_errors=True)
class TestClaudeCliBackendBare(unittest.TestCase):
"""Issue #68: --bare must be conditional on ANTHROPIC_API_KEY."""
def test_bare_included_when_api_key_set(self):
"""With ANTHROPIC_API_KEY, --bare should appear in the command."""
from skillopt_sleep.backend import ClaudeCliBackend
be = ClaudeCliBackend(claude_path="/usr/bin/false", timeout=5)
with unittest.mock.patch.dict(os.environ, {"ANTHROPIC_API_KEY": "sk-test"}):
# We can't run the real CLI, but we can inspect cmd construction
# by monkeypatching subprocess.run to capture the command.
captured = {}
def fake_run(cmd, **kwargs):
captured["cmd"] = cmd
class FakeProc:
stdout = "hello"
stderr = ""
returncode = 0
return FakeProc()
with unittest.mock.patch("subprocess.run", side_effect=fake_run):
be._call("test prompt")
self.assertIn("--bare", captured["cmd"])
def test_bare_omitted_without_api_key(self):
"""Without ANTHROPIC_API_KEY, --bare should NOT appear."""
from skillopt_sleep.backend import ClaudeCliBackend
be = ClaudeCliBackend(claude_path="/usr/bin/false", timeout=5)
env = os.environ.copy()
env.pop("ANTHROPIC_API_KEY", None)
with unittest.mock.patch.dict(os.environ, env, clear=True):
captured = {}
def fake_run(cmd, **kwargs):
captured["cmd"] = cmd
class FakeProc:
stdout = "hello"
stderr = ""
returncode = 0
return FakeProc()
with unittest.mock.patch("subprocess.run", side_effect=fake_run):
be._call("test prompt")
self.assertNotIn("--bare", captured["cmd"])
def test_cli_error_detected_and_logged(self):
"""Auth errors in CLI output should trigger a warning."""
from skillopt_sleep.backend import ClaudeCliBackend
be = ClaudeCliBackend(claude_path="/usr/bin/false", timeout=5)
captured = {}
def fake_run(cmd, **kwargs):
captured["cmd"] = cmd
class FakeProc:
stdout = "Not logged in · Please run /login"
stderr = ""
returncode = 0
return FakeProc()
with unittest.mock.patch.dict(os.environ, {}, clear=False):
with unittest.mock.patch("subprocess.run", side_effect=fake_run):
result = be._call("test prompt")
# The error string is returned as output (backwards-compat)
self.assertIn("Not logged in", result)
# But it's also recorded for detection
self.assertIn("Not logged in", getattr(be, "last_call_error", ""))
if __name__ == "__main__":
unittest.main(verbosity=2)