bb9bed82e1
ModelRole collapses to "primary": every role (main, tool selector, memory
agents, subagents, summarizer) resolves to the snapshot's frozen primary
model, per design 6.1/8.3 — users typically configure a single usable LLM,
so compile-time auxiliary bindings were bypassing run snapshots and
mis-attributing usage. Legacy auxiliary keys in stored snapshots, registry
JSON, and thread metadata are tolerated on read and dropped.
BREAKING CHANGE: ThreadModelSelection no longer carries an auxiliary ref;
snapshot selection_hash is computed over {primary, reasoning_effort} only;
ConfigurableModelMiddleware(role="auxiliary") is rejected.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
73 lines
2.5 KiB
Python
73 lines
2.5 KiB
Python
"""Tests for the CLI startup gates (design doc §10 step 4, §8.1).
|
|
|
|
``_startup_gates`` is the shared refuse-startup path for the chat entry and
|
|
``serve``: legacy model-configuration artifacts abort with the reset guide,
|
|
and a bootstrap registry aborts with ``MODEL_REGISTRY_NOT_READY`` — both
|
|
with exit code 1, never a silent fall-through to an implicit model.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
import typer
|
|
|
|
import EvoScientist.config.legacy_artifacts as legacy_artifacts
|
|
import EvoScientist.model_registry.runtime as runtime_module
|
|
from EvoScientist.cli.commands import _startup_gates
|
|
from EvoScientist.config.legacy_artifacts import LegacyArtifactsError
|
|
from EvoScientist.model_registry.errors import (
|
|
MODEL_REGISTRY_NOT_READY,
|
|
ModelRegistryError,
|
|
)
|
|
|
|
|
|
def test_legacy_artifacts_abort_startup(monkeypatch):
|
|
monkeypatch.setattr(
|
|
legacy_artifacts,
|
|
"assert_no_legacy_artifacts",
|
|
MagicMock(side_effect=LegacyArtifactsError("legacy providers.yaml found")),
|
|
)
|
|
with pytest.raises(typer.Exit) as excinfo:
|
|
_startup_gates()
|
|
assert excinfo.value.exit_code == 1
|
|
|
|
|
|
def test_bootstrap_registry_aborts_startup(monkeypatch):
|
|
monkeypatch.setattr(
|
|
legacy_artifacts, "assert_no_legacy_artifacts", MagicMock()
|
|
)
|
|
runtime = MagicMock()
|
|
runtime.registry_default.side_effect = ModelRegistryError(
|
|
MODEL_REGISTRY_NOT_READY, "bootstrap"
|
|
)
|
|
monkeypatch.setattr(runtime_module, "get_snapshot_runtime", lambda: runtime)
|
|
with pytest.raises(typer.Exit) as excinfo:
|
|
_startup_gates()
|
|
assert excinfo.value.exit_code == 1
|
|
|
|
|
|
def test_clean_environment_passes(monkeypatch):
|
|
monkeypatch.setattr(
|
|
legacy_artifacts, "assert_no_legacy_artifacts", MagicMock()
|
|
)
|
|
runtime = MagicMock()
|
|
runtime.registry_default.return_value = (MagicMock(), 2)
|
|
monkeypatch.setattr(runtime_module, "get_snapshot_runtime", lambda: runtime)
|
|
_startup_gates() # must not raise
|
|
|
|
|
|
def test_non_bootstrap_registry_error_propagates(monkeypatch):
|
|
"""Only NOT_READY is converted to a guided exit; other errors bubble up."""
|
|
monkeypatch.setattr(
|
|
legacy_artifacts, "assert_no_legacy_artifacts", MagicMock()
|
|
)
|
|
runtime = MagicMock()
|
|
runtime.registry_default.side_effect = ModelRegistryError(
|
|
"SNAPSHOT_EXPIRED", "expired"
|
|
)
|
|
monkeypatch.setattr(runtime_module, "get_snapshot_runtime", lambda: runtime)
|
|
with pytest.raises(ModelRegistryError):
|
|
_startup_gates()
|