@@ -465,6 +465,53 @@ def test_build_claude_runtime_with_local_login_stages_managed_env_and_prompt(mon
465465 assert payload ["mcpServers" ]["lean-lsp" ]["env" ]["LEAN_PROJECT_PATH" ] == str (shared_bundle .project .lean_root )
466466
467467
468+ def test_build_claude_runtime_falls_back_to_macos_keychain_when_credentials_file_absent (monkeypatch , tmp_path : Path ):
469+ shared_bundle = _shared_bundle (tmp_path )
470+ workflow = _workflow ("/prove" , "File.lean" )
471+ installed_plugin_root = (
472+ tmp_path
473+ / "managed"
474+ / "claude-home"
475+ / ".claude"
476+ / "plugins"
477+ / "cache"
478+ / "lean4-skills"
479+ / "lean4"
480+ / "4.4.0"
481+ )
482+ (installed_plugin_root / "skills" / "lean4" ).mkdir (parents = True )
483+ (installed_plugin_root / "skills" / "lean4" / "SKILL.md" ).write_text ("# Lean4\n " , encoding = "utf-8" )
484+ keychain_payload = json .dumps ({"claudeAiOauth" : {"accessToken" : "sk-ant-oat01-test" }})
485+ monkeypatch .setattr (autoformalize , "_require_executable" , lambda name , _msg , _env : f"/usr/bin/{ name } " )
486+ monkeypatch .setattr (autoformalize , "_claude_permission_args" , lambda : ("--dangerously-skip-permissions" ,))
487+ monkeypatch .setattr (autoformalize , "_install_managed_claude_plugin" , lambda ** _kwargs : installed_plugin_root )
488+ monkeypatch .setattr (autoformalize , "_read_keychain_claude_credentials" , lambda : keychain_payload )
489+
490+ runtime = autoformalize ._build_claude_runtime (
491+ auth_mode = "auto" ,
492+ user_instruction = workflow .workflow_args ,
493+ workflow = workflow ,
494+ base_environment = {"HOME" : str (shared_bundle .real_home ), "PATH" : "/usr/bin" },
495+ include_persisted_env = False ,
496+ shared_bundle = shared_bundle ,
497+ )
498+
499+ managed_credentials = runtime .managed_context .backend_home / ".claude" / ".credentials.json"
500+ assert managed_credentials .exists ()
501+ staged = json .loads (managed_credentials .read_text (encoding = "utf-8" ))
502+ assert staged ["claudeAiOauth" ]["accessToken" ] == "sk-ant-oat01-test"
503+ assert "ANTHROPIC_API_KEY" not in runtime .child_env
504+
505+
506+ def test_has_local_claude_login_detects_macos_keychain_when_credentials_file_absent (monkeypatch , tmp_path : Path ):
507+ real_home = tmp_path / "real-home"
508+ real_home .mkdir ()
509+ keychain_payload = json .dumps ({"claudeAiOauth" : {"accessToken" : "sk-ant-oat01-test" }})
510+ monkeypatch .setattr (autoformalize , "_read_keychain_claude_credentials" , lambda : keychain_payload )
511+
512+ assert autoformalize ._has_local_claude_login (real_home ) is True
513+
514+
468515def test_build_claude_runtime_accepts_anthropic_api_key (monkeypatch , tmp_path : Path ):
469516 shared_bundle = _shared_bundle (tmp_path )
470517 workflow = _workflow ("/formalize" )
@@ -483,6 +530,7 @@ def test_build_claude_runtime_accepts_anthropic_api_key(monkeypatch, tmp_path: P
483530 monkeypatch .setattr (autoformalize , "_require_executable" , lambda name , _msg , _env : f"/usr/bin/{ name } " )
484531 monkeypatch .setattr (autoformalize , "_claude_permission_args" , lambda : ("--dangerously-skip-permissions" ,))
485532 monkeypatch .setattr (autoformalize , "_install_managed_claude_plugin" , lambda ** _kwargs : installed_plugin_root )
533+ monkeypatch .setattr (autoformalize , "_read_keychain_claude_credentials" , lambda : None )
486534
487535 runtime = autoformalize ._build_claude_runtime (
488536 auth_mode = "auto" ,
0 commit comments