A fresh clone with no RESEARCH_LLM_API_KEY reported 19 pytest failures,
which reads as broken software on first contact.
Root cause was one line, not nineteen bugs. The OpenAI SDK raises
`OpenAIError: Missing credentials` at client CONSTRUCTION on an empty key,
and OpenRouterClient.__init__ builds its AsyncOpenAI eagerly — so every
object owning a client became un-constructible. Pure unit tests were the
visible casualty: test_synthesis.py::TestSynthesisEngine::test_init_defaults
is marked @pytest.mark.unit, takes no fixture, and does nothing but
`SynthesisEngine()` before asserting two floats. It raised on line one.
Construction was never the intended guard: settings.require_llm_key()
already fail-fasts at the MCP and REST entrypoints (run_mcp.py calls it),
which is where a missing key should surface. Now passes a "not-configured"
placeholder when no key is set; a real request fails with a normal upstream
401 instead of an import-time crash. require_llm_key() is unchanged and
still raises.
Setting a dummy key is NOT equivalent: it satisfies the live_llm guard in
conftest.py, un-skipping 31 live tests that then fail against the fake
credential (18 failed / 624 passed / 21 skipped). The placeholder is
internal, so RESEARCH_LLM_API_KEY stays unset and those tests keep skipping.
Keyless suite: 19 failed / 590 passed / 52 skipped -> 611 passed / 52
skipped. Skip count unchanged; every previously-passing test still passes
(590 + 19 + 2 new = 611).
Also covers REST fail-open on /synthesize/enhanced and /synthesize/p1 —
both already routed PARTIAL-with-zero-good through apply_fail_open() so
synthesis proceeds with a low-relevance caveat rather than refusing, but
that shipped untested end-to-end.
No public-contract change (no schema, citation shape, verifier semantics,
cache key, or output change) -> PATCH.