"""Behavioral checks for keyless supervision and evidence of completion.""" import importlib.util import json import os from pathlib import Path import pytest spec = importlib.util.spec_from_file_location("opencode_config", Path(__file__).parents[1] / "opencode_config.py") module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) def test_coach_uses_muse_only_without_authentication(): # The AltoClef coach runs SOLELY on the free OpenCode Muse model: default # and small models are Muse, and no Mine/Qwen provider exists here, so no # title/small task can fall back to Mine. No key material anywhere. config = module.configuration() assert config["enabled_providers"] == ["opencode"] assert config["model"] == "opencode/muse-spark-1.3-contributor-free" assert config["small_model"] == "opencode/muse-spark-1.3-contributor-free" assert module.MUSE_MODEL == config["model"] assert "mine" not in config.get("provider", {}) dumped = json.dumps(config) assert "apiKey" not in dumped assert "Authorization" not in dumped assert "qwen" not in dumped.lower() and "granite" not in dumped.lower() def test_coach_uses_builtin_plan_agent_only(): # Probed 2026-10-03 against Zen (keyless, isolated HOME, empty stdin): # the tested override shapes reproduce # `FreeTierError: ... can only be used from within OpenCode` (403): # prompt override, tools lockdown, blanket permission deny, even a # read-only permission shape. Bare description+mode and the built-ins # (build, plan) succeed. So there is no custom agent at all: the # built-in `plan` agent (read-only by design, official Muse system # prompt intact) supervises from user-level instructions. config = module.configuration() assert config["default_agent"] == "plan" assert "agent" not in config def test_coach_instructions_live_at_user_level(): message = module.coach_user_message({"connected": True, "task_active": True}) assert "reply only with one compact json object" in message.lower() assert '"connected": true' in message assert "no tools" in message.lower() def test_credentials_are_not_inherited(monkeypatch, tmp_path): for name in ["GEMINI_BOT_KEY_1", "OPENAI_API_KEY", "VOYAGER_API_KEY", "HF_TOKEN"]: monkeypatch.setenv(name, "private-secret") monkeypatch.setenv("PATH", "executable-search-path") env = module.child_environment(tmp_path) assert env["PATH"] == "executable-search-path" assert not any(env.get(name) for name in ["GEMINI_BOT_KEY_1", "OPENAI_API_KEY", "VOYAGER_API_KEY", "HF_TOKEN"]) assert env["XDG_DATA_HOME"] == str(tmp_path / "data") def event(text): return json.dumps({"type":"text","part":{"text":text}}) def test_accepts_fragmented_opencode_decision(): output = "\n".join([event('{"action":"start",'), event('"reason":"Task is inactive"}')]) assert module.parse_decision(output)["action"] == "start" @pytest.mark.parametrize("text", ['{"action":"shell","reason":"run a command"}', 'Dragon defeated!', '{}', '[]']) def test_rejects_unsupported_or_invented_decisions(text): with pytest.raises(RuntimeError): module.parse_decision(event(text)) def test_provider_error_is_not_a_valid_decision(): with pytest.raises(RuntimeError): module.parse_decision(json.dumps({"type":"error","error":{"message":"quota exceeded"}})) @pytest.mark.parametrize("observation", [ {"connected":False, "task_active":False}, {"entered_end":True, "dragon_seen":False, "task_active":False}, {"entered_end":True, "returned_from_end":True}, {"credits_observed":True}, ]) def test_disconnect_or_unloaded_dragon_never_proves_completion(observation): assert not module.goal_completed(observation) def test_credits_after_entering_end_prove_completion(): assert module.goal_completed({"entered_end":True,"credits_observed":True})