forked from molecule-ai/molecule-core
Closes task #123 — last piece of #87 cleanup. Pre-fix: workspace/preflight.py:11 hardcoded a tuple of "supported" runtime names (claude-code, codex, ollama, langgraph, etc.). Every new template repo required a code change in molecule-runtime to be recognized — direct violation of the universal-runtime principle (#87) where adapters declare themselves and the runtime stays generic. Post-fix: discovery-based validation via the same ADAPTER_MODULE env var that production load paths already consult (workspace/adapters/__init__.py:get_adapter). Distinguished failure modes so operator messages are concrete: - ADAPTER_MODULE unset → "no adapter installed; set the env var" - ADAPTER_MODULE set but module won't import → import error type + message - module imports but no Adapter class → "convention violation, add `Adapter = YourClass`" - Adapter.name() raises → caught with operator message - Adapter.name() returns non-string → contract violation message - Adapter.name() doesn't match config.runtime → drift WARNING (not fatal; the adapter wins in production, config.yaml is just documentation) The drift case is the one behavioral change worth calling out: the prior static-list path would have hard-failed config.runtime values not in the allowlist. With discovery, an unknown runtime in config.yaml is just a documentation drift — the adapter that's actually installed runs regardless. Operator gets a warning naming both the configured and installed names so they can fix whichever is stale. Tests: - Replaces the obsolete "static list pass/fail" tests with 6 new cases covering each distinguished failure mode, plus a positive test for the adapter-matches-config happy path - Adds an autouse `_default_langgraph_adapter` fixture that pre-installs a fake adapter via sys.modules monkey-patching, so existing tests building default WorkspaceConfig (runtime="langgraph") inherit a valid adapter without each test setting ADAPTER_MODULE - Failure-mode tests opt out of the default fixture via @pytest.mark.no_default_adapter (registered in pytest.ini) - Sentinel pattern (`_UNSET = object()`) for `name_returns` so None is a passable test value (otherwise `is not None` would skip the None branch — exact bug the sentinel avoids) Verification: - 22/22 preflight tests pass (was 16; +6 new failure-path tests) - 1256/1256 workspace pytest pass (was 1251; +5 net) - No production code path other than preflight changed Source: 2026-04-27 #87 cleanup audit after PR #2154 (wedge extraction). This change is independent of the cli_executor.py template moves (task #122) — completes one of the two remaining cleanup items. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| adapters | ||
| __init__.py | ||
| conftest.py | ||
| test_a2a_cli.py | ||
| test_a2a_client.py | ||
| test_a2a_executor.py | ||
| test_a2a_mcp_server.py | ||
| test_a2a_tools_impl.py | ||
| test_a2a_tools_module.py | ||
| test_agent_base_urls.py | ||
| test_agent.py | ||
| test_agents_md.py | ||
| test_approval.py | ||
| test_audit_ledger.py | ||
| test_audit.py | ||
| test_awareness_client_full.py | ||
| test_claude_sdk_executor.py | ||
| test_compliance.py | ||
| test_config.py | ||
| test_consolidation.py | ||
| test_coordinator_parent.py | ||
| test_coordinator_routing.py | ||
| test_delegation.py | ||
| test_events.py | ||
| test_executor_helpers.py | ||
| test_gh_wrapper.sh | ||
| test_governance.py | ||
| test_heartbeat_runtime_metadata.py | ||
| test_heartbeat.py | ||
| test_hitl.py | ||
| test_main_initial_prompt.py | ||
| test_mcp_memory.py | ||
| test_memory.py | ||
| test_molecule_ai_status.py | ||
| test_namespaces.py | ||
| test_openclaw_adapter.py | ||
| test_platform_auth.py | ||
| test_plugins_builtins.py | ||
| test_plugins_registry.py | ||
| test_plugins.py | ||
| test_pre_stop.py | ||
| test_preflight.py | ||
| test_prompt.py | ||
| test_routing_policy.py | ||
| test_runtime_capabilities.py | ||
| test_runtime_wedge.py | ||
| test_safe_env.py | ||
| test_sandbox.py | ||
| test_secret_redact.py | ||
| test_security_scan.py | ||
| test_skills_loader.py | ||
| test_skills_watcher.py | ||
| test_snapshot_scrub.py | ||
| test_telemetry.py | ||
| test_temporal_workflow.py | ||
| test_transcript_auth.py | ||
| test_watcher.py | ||