mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-03 04:07:04 +00:00
An owner draft handed to the preset compiler is a validation input, not a roster the preset owns: reviewer seats on that path stay self-contained inline routes again (the base behavior), and subagent_id references with row minting remain exclusive to the roster the preset itself ships. Full-suite repins for the retired name field ride along: canonical round-trip and write-seam stop expecting fabricated names, the receipt owner-edit trigger moves to a semantic field, the Agents panel order pin follows the dependency direction, and the onboarding endpoint pins read reviewer references through the saved roster. Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
1450 lines
62 KiB
Python
1450 lines
62 KiB
Python
"""POST /api/onboarding/complete — the ONE atomic onboarding transaction (D-8).
|
|
|
|
These tests use the REAL persist path against an isolated settings file, so
|
|
"nothing was saved" is proved by the file on disk rather than by a mock that was
|
|
not called.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
import pytest
|
|
from starlette.applications import Starlette
|
|
from starlette.routing import Route
|
|
from starlette.testclient import TestClient
|
|
|
|
from ouroboros.settings_setup_contract import ONBOARDING_COMPLETED_KEY
|
|
from ouroboros.subscription_install_presets import PRESET_MARKER_KEY
|
|
from ouroboros.configured_subagents import SUBAGENTS_RECEIPT_KEY, SUBAGENTS_SETTING
|
|
|
|
_PROVIDER_ENV = (
|
|
"OPENROUTER_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "MINIMAX_API_KEY",
|
|
"CLOUDRU_FOUNDATION_MODELS_API_KEY", "GIGACHAT_CREDENTIALS", "GIGACHAT_USER",
|
|
"GIGACHAT_PASSWORD", "OPENAI_COMPATIBLE_BASE_URL", "OPENAI_BASE_URL",
|
|
"USE_LOCAL_MAIN", "USE_LOCAL_HEAVY", "USE_LOCAL_LIGHT", "USE_LOCAL_FALLBACK",
|
|
"USE_LOCAL_CONSCIOUSNESS", "LOCAL_MODEL_SOURCE", PRESET_MARKER_KEY,
|
|
"OUROBOROS_REVIEWER_SLOTS", SUBAGENTS_SETTING, "OUROBOROS_SUBAGENT_HARNESS",
|
|
"OUROBOROS_SAFETY_MODE",
|
|
ONBOARDING_COMPLETED_KEY,
|
|
)
|
|
|
|
# Shapes copied from the LIVE daemon (GET /v2/credential-profiles, GET
|
|
# /v2/agent-capabilities, 2026-08-09) through the same projection the accounts
|
|
# panel uses: a per-harness accounts row carrying the engine's own `next_up`
|
|
# routing pointer, and credential profiles carrying `credential_kind` +
|
|
# availability. A fake without `next_up` is a fake of an engine that does not
|
|
# exist.
|
|
def _native_account(harness, *, route="local_session", detected=True, enabled=True):
|
|
return {
|
|
"harness_id": harness,
|
|
"native_credentials_enabled": enabled,
|
|
"native_login_detected": detected,
|
|
"identity": {"email": "owner@example.com", "plan": "claude_max"},
|
|
"next_up": {"kind": "native", "route": route},
|
|
}
|
|
|
|
|
|
def _profile_account(harness, profile_id):
|
|
return {
|
|
"harness_id": harness,
|
|
"native_credentials_enabled": True,
|
|
"native_login_detected": False,
|
|
"identity": None,
|
|
"next_up": {"kind": "profile", "profileId": profile_id},
|
|
}
|
|
|
|
|
|
def _profile(harness, profile_id, *, kind="config_dir_login", enabled=True,
|
|
availability="available", verification="passed"):
|
|
return {
|
|
"profile": {"profile_id": profile_id, "harness_id": harness,
|
|
"display_name": profile_id, "credential_kind": kind,
|
|
"enabled": enabled},
|
|
"status": {"profile_id": profile_id, "harness_id": harness,
|
|
"availability": availability, "verification": verification,
|
|
"verification_source": "vendor"},
|
|
"identity": {"email": "owner@example.com", "plan": "pro"},
|
|
}
|
|
|
|
|
|
LIVE_SNAPSHOT = {
|
|
"daemon": {"state": "running"},
|
|
"harnesses": [
|
|
{
|
|
"id": "claude", "status": "ok", "enabled": True,
|
|
"access_profiles_supported": ["readonly", "workspace_write"],
|
|
"models": [{"id": "claude-opus-5"}, {"id": "claude-sonnet-5"},
|
|
{"id": "claude-fable-5"}, {"id": "claude-opus-4-6"}],
|
|
},
|
|
{
|
|
"id": "codex", "status": "ok", "enabled": True,
|
|
"models": [{"id": "gpt-5.6-sol"}, {"id": "gpt-5.6-terra"}, {"id": "gpt-5.5"}],
|
|
},
|
|
],
|
|
"profiles": {
|
|
"harnessAccounts": [_native_account("claude"), _native_account("codex")],
|
|
"profiles": [],
|
|
},
|
|
}
|
|
|
|
WIZARD_PAYLOAD = {
|
|
"OPENROUTER_API_KEY": "sk-or-v1-abcdefghijklmnop",
|
|
"OUROBOROS_MODEL": "openai/gpt-5.6-luna",
|
|
"OUROBOROS_REVIEW_ENFORCEMENT": "advisory",
|
|
"OUROBOROS_RUNTIME_MODE": "advanced",
|
|
"TOTAL_BUDGET": 25.0,
|
|
"OUROBOROS_PER_TASK_COST_USD": 5.0,
|
|
}
|
|
|
|
|
|
class _Harness:
|
|
"""One isolated onboarding server plus the calls it made."""
|
|
|
|
def __init__(self, client, settings_path, calls):
|
|
self.client = client
|
|
self.settings_path = settings_path
|
|
self.calls = calls
|
|
|
|
def saved(self) -> dict:
|
|
if not self.settings_path.exists():
|
|
return {}
|
|
return json.loads(self.settings_path.read_text(encoding="utf-8"))
|
|
|
|
|
|
@pytest.fixture
|
|
def onboarding(monkeypatch, tmp_path):
|
|
"""Build the endpoint over an isolated settings file (real writes)."""
|
|
import ouroboros.config as config
|
|
import ouroboros.gateway.onboarding as gw_onboarding
|
|
import ouroboros.gateway.settings as gw_settings
|
|
|
|
for key in _PROVIDER_ENV:
|
|
monkeypatch.delenv(key, raising=False)
|
|
settings_path = tmp_path / "settings.json"
|
|
monkeypatch.setattr(config, "DATA_DIR", tmp_path)
|
|
monkeypatch.setattr(config, "SETTINGS_PATH", settings_path)
|
|
|
|
calls: dict = {"snapshot": 0, "env": [], "supervisor": 0, "side_effects": 0}
|
|
|
|
def _snapshot():
|
|
calls["snapshot"] += 1
|
|
payload = calls.get("snapshot_payload", LIVE_SNAPSHOT)
|
|
if isinstance(payload, Exception):
|
|
raise payload
|
|
return payload
|
|
|
|
monkeypatch.setattr(gw_onboarding, "_read_harness_snapshot", _snapshot)
|
|
monkeypatch.setattr(config, "apply_settings_to_env",
|
|
lambda settings: calls["env"].append(dict(settings)))
|
|
|
|
def _supervisor(_request, _settings):
|
|
calls["supervisor"] += 1
|
|
return True
|
|
|
|
def _side_effects(*_a, **_k):
|
|
calls["side_effects"] += 1
|
|
|
|
monkeypatch.setattr(gw_settings, "_start_supervisor_if_needed_for_request", _supervisor)
|
|
monkeypatch.setattr(gw_settings, "_apply_settings_save_side_effects", _side_effects)
|
|
|
|
app = Starlette(routes=[
|
|
Route("/api/onboarding/subagents/preview",
|
|
endpoint=gw_onboarding.api_onboarding_subagents_preview, methods=["POST"]),
|
|
Route("/api/onboarding/complete",
|
|
endpoint=gw_onboarding.api_onboarding_complete, methods=["POST"]),
|
|
])
|
|
app.state.drive_root = tmp_path
|
|
app.state.repo_dir = tmp_path / "repo"
|
|
with TestClient(app) as client:
|
|
yield _Harness(client, settings_path, calls)
|
|
|
|
|
|
def test_fresh_install_applies_the_preset_in_one_write(onboarding):
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True},
|
|
)
|
|
|
|
assert response.status_code == 200, response.text
|
|
body = response.json()
|
|
assert body["ok"] is True
|
|
assert body["preset"]["applied"] is True
|
|
assert body["preset"]["reason"] == "applied"
|
|
assert body["preset"]["harnesses"] == ["claude", "codex"]
|
|
assert onboarding.calls["snapshot"] == 1 # exactly ONE daemon read
|
|
|
|
saved = onboarding.saved()
|
|
assert saved[PRESET_MARKER_KEY] == "3"
|
|
assert saved["OUROBOROS_SUBAGENT_HARNESS"] == ""
|
|
available = json.loads(saved[SUBAGENTS_SETTING])
|
|
# 4=A: the advisory seat's session route matches no task actor, so the
|
|
# preset mints a roster row for it and every reviewer slot is a
|
|
# subagent_id REFERENCE into the roster the preset ships.
|
|
roster = {row["subagent_id"]: row["route"]["target_id"] for row in available["items"]}
|
|
assert [row["route"]["target_id"] for row in available["items"]] == [
|
|
"claude=claude-opus-5", "codex=gpt-5.6-sol", "openai/gpt-5.6-luna",
|
|
"claude=claude-sonnet-5",
|
|
]
|
|
assert all("name" not in row for row in available["items"]) # retired (1=A)
|
|
assert json.loads(saved[SUBAGENTS_RECEIPT_KEY])["available_subagents_fingerprint"]
|
|
slots = json.loads(saved["OUROBOROS_REVIEWER_SLOTS"])
|
|
assert [roster[row["subagent_id"]] for row in slots["triad"]] == [
|
|
"claude=claude-opus-5", "codex=gpt-5.6-sol"]
|
|
assert roster[slots["scope"][0]["subagent_id"]] == "codex=gpt-5.6-sol"
|
|
assert roster[slots["advisory"]["subagent_id"]] == "claude=claude-sonnet-5"
|
|
# Everything else of the transaction landed in the SAME file.
|
|
assert saved["OUROBOROS_RUNTIME_MODE"] == "advanced"
|
|
assert saved["OPENROUTER_API_KEY"] == WIZARD_PAYLOAD["OPENROUTER_API_KEY"]
|
|
assert saved["OUROBOROS_SAFETY_MODE"] == "light" # fresh-install default
|
|
# D-2: the API model slots are untouched by the preset.
|
|
assert saved["OUROBOROS_MODEL"] == "openai/gpt-5.6-luna"
|
|
assert onboarding.calls["supervisor"] == 1
|
|
|
|
|
|
def test_antigravity_install_succeeds_without_inventing_reviewer_seats(onboarding):
|
|
onboarding.calls["snapshot_payload"] = {
|
|
"daemon": {"state": "running"},
|
|
"harnesses": [{
|
|
"id": "agy", "status": "ok", "enabled": True,
|
|
"models": [
|
|
{"id": "gemini-3.7-flash-low"},
|
|
{"id": "gemini-3.7-flash-medium"},
|
|
{"id": "gemini-3.7-flash-high"},
|
|
],
|
|
}],
|
|
"profiles": {
|
|
"harnessAccounts": [_profile_account("agy", "google-owner")],
|
|
"profiles": [_profile("agy", "google-owner", kind="oauth_token")],
|
|
},
|
|
}
|
|
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True},
|
|
)
|
|
|
|
assert response.status_code == 200, response.text
|
|
saved = onboarding.saved()
|
|
items = json.loads(saved[SUBAGENTS_SETTING])["items"]
|
|
assert items[0]["route"] == {
|
|
"kind": "agent_session",
|
|
"target_id": "agy=gemini-3.7-flash-high",
|
|
"credential_profile_id": "",
|
|
}
|
|
assert saved["OUROBOROS_REVIEWER_SLOTS"] == ""
|
|
|
|
|
|
def test_daemon_unavailable_persists_nothing_and_keeps_the_wizard_open(onboarding):
|
|
onboarding.calls["snapshot_payload"] = {
|
|
"daemon": {"state": "unreachable", "last_error": "connect_failed: boom"},
|
|
"harnesses": [], "profiles": {},
|
|
}
|
|
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True},
|
|
)
|
|
|
|
assert response.status_code == 503, response.text
|
|
body = response.json()
|
|
assert body["code"] == "daemon_unavailable"
|
|
assert body["can_skip"] is True
|
|
assert body["saved"] is False
|
|
assert "could not be applied" in body["error"]
|
|
# NOTHING was written: no settings file, no supervisor, no env projection.
|
|
assert not onboarding.settings_path.exists()
|
|
assert onboarding.calls["supervisor"] == 0
|
|
assert onboarding.calls["env"] == []
|
|
|
|
|
|
def test_unresolvable_model_refuses_before_any_write(onboarding):
|
|
onboarding.calls["snapshot_payload"] = {
|
|
"daemon": {"state": "running"},
|
|
"harnesses": [{"id": "claude", "status": "ok", "enabled": True,
|
|
"models": [{"id": "claude-sonnet-5"}]}],
|
|
"profiles": {"harnessAccounts": [_native_account("claude")]},
|
|
}
|
|
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True},
|
|
)
|
|
|
|
assert response.status_code == 503, response.text
|
|
assert response.json()["code"] == "model_not_in_discovery"
|
|
assert not onboarding.settings_path.exists()
|
|
|
|
|
|
def test_a_signed_in_account_without_discovery_is_a_typed_failure(onboarding):
|
|
onboarding.calls["snapshot_payload"] = {
|
|
"daemon": {"state": "running"},
|
|
"harnesses": [{"id": "claude", "status": "ok", "enabled": True,
|
|
"models": [], "models_error": "harness_unavailable"}],
|
|
"profiles": {"harnessAccounts": [_native_account("claude")]},
|
|
}
|
|
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True},
|
|
)
|
|
|
|
assert response.status_code == 503
|
|
assert response.json()["code"] == "models_unavailable"
|
|
assert not onboarding.settings_path.exists()
|
|
|
|
|
|
def test_skip_flag_completes_without_any_preset_key(onboarding):
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True,
|
|
"skipSubscriptionPresets": True},
|
|
)
|
|
|
|
assert response.status_code == 200, response.text
|
|
assert response.json()["preset"] == {
|
|
"applied": False, "reason": "skipped_by_owner", "harnesses": [], "receipt": {}}
|
|
assert onboarding.calls["snapshot"] == 0 # the daemon is never even asked
|
|
|
|
saved = onboarding.saved()
|
|
assert saved # onboarding DID complete
|
|
assert not saved.get(PRESET_MARKER_KEY)
|
|
assert not saved.get("OUROBOROS_REVIEWER_SLOTS")
|
|
assert not saved.get("OUROBOROS_SUBAGENT_HARNESS")
|
|
assert onboarding.calls["supervisor"] == 1
|
|
|
|
|
|
def test_no_subscription_declared_never_reads_the_daemon(onboarding):
|
|
response = onboarding.client.post("/api/onboarding/complete", json=dict(WIZARD_PAYLOAD))
|
|
|
|
assert response.status_code == 200, response.text
|
|
assert response.json()["preset"]["reason"] == "applied"
|
|
assert onboarding.calls["snapshot"] == 0
|
|
assert json.loads(onboarding.saved()[SUBAGENTS_SETTING])["items"]
|
|
|
|
|
|
def test_api_only_preview_is_read_only_and_never_reads_claudexor(onboarding):
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/subagents/preview", json=dict(WIZARD_PAYLOAD),
|
|
)
|
|
|
|
assert response.status_code == 200, response.text
|
|
body = response.json()
|
|
assert body["source"] == "onboarding_default"
|
|
assert body["available_subagents"]["enabled"] is True
|
|
assert [row["code"] for row in body["diagnostics"]] == [
|
|
"duplicate_api_routes_omitted",
|
|
]
|
|
assert onboarding.calls["snapshot"] == 0
|
|
assert not onboarding.settings_path.exists()
|
|
assert onboarding.calls["env"] == []
|
|
assert onboarding.calls["supervisor"] == 0
|
|
|
|
|
|
def test_preview_does_not_persist_unrelated_context_mode_compat(onboarding):
|
|
legacy_bytes = (
|
|
b'{"OUROBOROS_CONTEXT_MODE":"low",'
|
|
b'"OPENROUTER_API_KEY":"sk-or-v1-existing-owner-key"}\n'
|
|
)
|
|
onboarding.settings_path.write_bytes(legacy_bytes)
|
|
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/subagents/preview", json=dict(WIZARD_PAYLOAD),
|
|
)
|
|
|
|
assert response.status_code == 200, response.text
|
|
assert onboarding.settings_path.read_bytes() == legacy_bytes
|
|
assert onboarding.calls["snapshot"] == 0
|
|
assert onboarding.calls["env"] == []
|
|
assert onboarding.calls["supervisor"] == 0
|
|
|
|
|
|
def test_local_only_preview_materializes_only_local_routes_without_daemon_read(onboarding):
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/subagents/preview",
|
|
json={
|
|
**WIZARD_PAYLOAD,
|
|
"OPENROUTER_API_KEY": "",
|
|
"LOCAL_MODEL_SOURCE": "/models/owner.gguf",
|
|
"LOCAL_ROUTING_MODE": "all",
|
|
"OUROBOROS_MODEL": "owner-main",
|
|
"OUROBOROS_MODEL_LIGHT": "owner-light",
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 200, response.text
|
|
targets = [
|
|
row["route"]["target_id"]
|
|
for row in response.json()["available_subagents"]["items"]
|
|
]
|
|
assert targets == ["owner-main (local)", "owner-light (local)"]
|
|
assert onboarding.calls["snapshot"] == 0
|
|
assert not onboarding.settings_path.exists()
|
|
|
|
|
|
def test_subscription_preview_reads_one_snapshot_and_still_persists_nothing(onboarding):
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/subagents/preview",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True},
|
|
)
|
|
|
|
assert response.status_code == 200, response.text
|
|
assert onboarding.calls["snapshot"] == 1
|
|
assert not onboarding.settings_path.exists()
|
|
assert onboarding.calls["supervisor"] == 0
|
|
|
|
|
|
def test_completion_validates_but_never_replaces_owner_edited_actor_rows(onboarding):
|
|
owner = {
|
|
"enabled": True,
|
|
"items": [{
|
|
"subagent_id": "owner-route",
|
|
"name": "Owner route",
|
|
"recommended_use": "Use exactly when the owner chooses this saved route.",
|
|
"route": {"kind": "api_model", "target_id": "x-ai/grok-owner"},
|
|
"effort": "high",
|
|
}],
|
|
}
|
|
preview = onboarding.client.post(
|
|
"/api/onboarding/subagents/preview",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True,
|
|
SUBAGENTS_SETTING: owner},
|
|
)
|
|
assert preview.status_code == 200, preview.text
|
|
assert preview.json()["source"] == "configured"
|
|
expected = {
|
|
"enabled": True,
|
|
"items": [{k: v for k, v in owner["items"][0].items() if k != "name"}],
|
|
}
|
|
assert preview.json()["available_subagents"] == expected
|
|
assert not onboarding.settings_path.exists()
|
|
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True,
|
|
SUBAGENTS_SETTING: owner},
|
|
)
|
|
assert response.status_code == 200, response.text
|
|
assert json.loads(onboarding.saved()[SUBAGENTS_SETTING]) == expected
|
|
assert response.json()["preset"]["receipt"]["source"] == "configured"
|
|
assert onboarding.calls["snapshot"] == 2 # one independently verified snapshot per request
|
|
|
|
|
|
def test_malformed_owner_actor_draft_refuses_before_snapshot_or_write(onboarding):
|
|
malformed = {
|
|
"enabled": True,
|
|
"items": [{
|
|
"subagent_id": "dup",
|
|
"name": "Bad",
|
|
"recommended_use": "Bad",
|
|
"route": {"kind": "api_model", "target_id": "x", "extra": True},
|
|
}],
|
|
}
|
|
for path in ("/api/onboarding/subagents/preview", "/api/onboarding/complete"):
|
|
response = onboarding.client.post(
|
|
path,
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True,
|
|
SUBAGENTS_SETTING: malformed},
|
|
)
|
|
assert response.status_code == 400, response.text
|
|
assert response.json()["code"] == "invalid_available_subagents"
|
|
assert onboarding.calls["snapshot"] == 0
|
|
assert not onboarding.settings_path.exists()
|
|
|
|
|
|
def test_skip_subscription_defaults_still_saves_an_explicit_owner_actor_draft(onboarding):
|
|
owner = {
|
|
"enabled": False,
|
|
"items": [],
|
|
}
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True,
|
|
"skipSubscriptionPresets": True, SUBAGENTS_SETTING: owner},
|
|
)
|
|
|
|
assert response.status_code == 200, response.text
|
|
assert response.json()["preset"]["reason"] == "configured_by_owner"
|
|
saved = onboarding.saved()
|
|
assert json.loads(saved[SUBAGENTS_SETTING]) == owner
|
|
assert not saved.get(PRESET_MARKER_KEY)
|
|
assert not saved.get("OUROBOROS_REVIEWER_SLOTS")
|
|
assert onboarding.calls["snapshot"] == 0
|
|
|
|
|
|
def test_an_old_unconfigured_install_is_not_retro_presetted(onboarding):
|
|
"""The reviewer's probe: a long-lived install whose provider stopped working.
|
|
|
|
"No startup-ready provider" is true for it too, so that predicate alone made
|
|
the install-time window RE-OPEN and wrote the preset over reviewer/subagent
|
|
choices the owner made themselves (D-4). A settings file that already exists
|
|
is proof this is not a first run."""
|
|
onboarding.settings_path.write_text(json.dumps({
|
|
"OUROBOROS_MODEL": "openai/gpt-5.6-luna",
|
|
"OUROBOROS_REVIEWER_SLOTS": '{"triad": [{"route": {"target_id": "mine"}}]}',
|
|
"OUROBOROS_SUBAGENT_HARNESS": "claude=claude-sonnet-5",
|
|
"OUROBOROS_SAFETY_MODE": "full",
|
|
}), encoding="utf-8")
|
|
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True},
|
|
)
|
|
|
|
assert response.status_code == 200, response.text
|
|
assert response.json()["preset"] == {
|
|
"applied": False, "reason": "not_install_time", "harnesses": [], "receipt": {}}
|
|
assert onboarding.calls["snapshot"] == 0 # the daemon is not even asked
|
|
saved = onboarding.saved()
|
|
assert not saved.get(PRESET_MARKER_KEY)
|
|
# The owner's OWN reviewer/subagent configuration survived untouched.
|
|
assert saved["OUROBOROS_REVIEWER_SLOTS"] == '{"triad": [{"route": {"target_id": "mine"}}]}'
|
|
assert saved["OUROBOROS_SUBAGENT_HARNESS"] == "claude=claude-sonnet-5"
|
|
assert saved["OUROBOROS_SAFETY_MODE"] == "full"
|
|
|
|
|
|
def test_nonfresh_recovery_completion_still_saves_an_explicit_owner_draft(onboarding):
|
|
onboarding.settings_path.write_text(json.dumps({
|
|
"OUROBOROS_MODEL": "openai/gpt-5.6-luna",
|
|
SUBAGENTS_SETTING: "",
|
|
"OUROBOROS_REVIEWER_SLOTS": "owner-reviewer-bytes",
|
|
}), encoding="utf-8")
|
|
owner = {
|
|
"enabled": True,
|
|
"items": [{
|
|
"subagent_id": "owner-route",
|
|
"name": "Owner route",
|
|
"recommended_use": "Use this exact recovery draft.",
|
|
"route": {"kind": "api_model", "target_id": "openai/gpt-5.6-sol"},
|
|
}],
|
|
}
|
|
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={
|
|
**WIZARD_PAYLOAD,
|
|
"subscriptionsConnected": True,
|
|
SUBAGENTS_SETTING: owner,
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 200, response.text
|
|
assert response.json()["preset"]["reason"] == "configured_by_owner"
|
|
assert response.json()["preset"]["applied"] is True
|
|
assert onboarding.calls["snapshot"] == 0
|
|
saved = onboarding.saved()
|
|
expected = {
|
|
"enabled": True,
|
|
"items": [{k: v for k, v in owner["items"][0].items() if k != "name"}],
|
|
}
|
|
assert json.loads(saved[SUBAGENTS_SETTING]) == expected
|
|
assert saved["OUROBOROS_REVIEWER_SLOTS"] == "owner-reviewer-bytes"
|
|
assert not saved.get(PRESET_MARKER_KEY)
|
|
|
|
|
|
def test_every_completion_records_the_durable_onboarding_fact(onboarding):
|
|
"""Including one that connected nothing: absence of a provider must never be
|
|
able to re-open the install-time window later."""
|
|
response = onboarding.client.post("/api/onboarding/complete", json=dict(WIZARD_PAYLOAD))
|
|
|
|
assert response.status_code == 200, response.text
|
|
assert response.json()["preset"]["reason"] == "applied"
|
|
assert onboarding.saved()[ONBOARDING_COMPLETED_KEY]
|
|
|
|
|
|
def test_a_recorded_completion_closes_the_window_even_without_a_settings_file(onboarding):
|
|
"""The marker is the durable fact, checked before anything else: an install
|
|
that once completed onboarding is not install-time again."""
|
|
from ouroboros.gateway.onboarding import preset_eligible
|
|
|
|
assert preset_eligible({}) is True
|
|
assert preset_eligible({ONBOARDING_COMPLETED_KEY: "2026-08-09T00:00:00Z"}) is False
|
|
assert preset_eligible({PRESET_MARKER_KEY: "1"}) is False
|
|
|
|
|
|
def test_an_environment_completion_fact_cannot_close_the_install_window(monkeypatch,
|
|
onboarding):
|
|
"""FINDING 1 at the onboarding endpoint, exactly as probed.
|
|
|
|
The completion fact and the preset marker are supposed to be authored by this
|
|
endpoint alone, and the request-body merge skip enforced that for the BODY.
|
|
The environment was still an authority: `load_settings` overlaid an
|
|
environment timestamp, so on a genuinely FRESH install (no settings.json,
|
|
connected subscriptions) the endpoint answered `not_install_time`, made ZERO
|
|
daemon calls, and closed the onboarding window for good without ever
|
|
installing the presets the owner connected accounts for."""
|
|
monkeypatch.setenv(ONBOARDING_COMPLETED_KEY, "2020-01-01T00:00:00Z")
|
|
monkeypatch.setenv(PRESET_MARKER_KEY, "1")
|
|
assert not onboarding.settings_path.exists()
|
|
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True})
|
|
|
|
assert response.status_code == 200, response.text
|
|
body = response.json()
|
|
assert body["preset"]["reason"] == "applied", body
|
|
assert body["preset"]["applied"] is True
|
|
assert onboarding.calls["snapshot"] == 1, "the daemon was never consulted"
|
|
saved = onboarding.saved()
|
|
assert saved[PRESET_MARKER_KEY] == "3"
|
|
# The endpoint's own timestamp, not the environment's.
|
|
assert saved[ONBOARDING_COMPLETED_KEY] != "2020-01-01T00:00:00Z"
|
|
|
|
|
|
def test_onboarding_validation_refusals_say_saved_false(onboarding):
|
|
"""FINDING 2 on this surface: an early validation refusal answered a bare
|
|
`{"error": ...}`, so a client could not tell it apart from an old envelope."""
|
|
malformed = onboarding.client.post("/api/onboarding/complete", json=["nope"])
|
|
assert malformed.status_code == 400, malformed.text
|
|
assert malformed.json()["saved"] is False, malformed.text
|
|
|
|
no_provider = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={"OUROBOROS_RUNTIME_MODE": "advanced", "subscriptionsConnected": True})
|
|
assert no_provider.status_code == 400, no_provider.text
|
|
assert no_provider.json()["saved"] is False, no_provider.text
|
|
assert not onboarding.settings_path.exists()
|
|
|
|
|
|
def test_subscription_free_completion_still_materializes_actor_default(onboarding):
|
|
"""API/local-only first installs compile actors without reading Claudexor."""
|
|
response = onboarding.client.post("/api/onboarding/complete", json=dict(WIZARD_PAYLOAD))
|
|
assert response.status_code == 200, response.text
|
|
assert response.json()["preset"]["applied"] is True
|
|
saved = onboarding.saved()
|
|
assert saved[ONBOARDING_COMPLETED_KEY], "the completion fact IS unconditional"
|
|
assert saved.get(PRESET_MARKER_KEY) == "3"
|
|
assert json.loads(saved[SUBAGENTS_SETTING])["items"]
|
|
|
|
|
|
def test_generic_settings_save_cannot_author_or_clear_the_completion_fact():
|
|
from ouroboros.gateway.settings import _merge_settings_payload
|
|
|
|
merged = _merge_settings_payload({ONBOARDING_COMPLETED_KEY: "2026-08-09T00:00:00Z"},
|
|
{ONBOARDING_COMPLETED_KEY: ""})
|
|
assert merged[ONBOARDING_COMPLETED_KEY] == "2026-08-09T00:00:00Z"
|
|
fresh = _merge_settings_payload({}, {ONBOARDING_COMPLETED_KEY: "2026-08-09T00:00:00Z"})
|
|
assert not fresh.get(ONBOARDING_COMPLETED_KEY)
|
|
|
|
|
|
def test_a_contended_lock_writes_nothing_and_starts_nothing(onboarding):
|
|
"""FINDING 1 (the class): `_acquire_settings_lock` answers None on timeout.
|
|
|
|
The write used to run the precondition and `atomic_write_json` ANYWAY, so a
|
|
contended save was the one save that skipped the check it advertises. The
|
|
lock is now a precondition of the write itself."""
|
|
import os
|
|
|
|
lock_path = onboarding.settings_path.with_name(onboarding.settings_path.name + ".lock")
|
|
foreign_fd = os.open(str(lock_path), os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o644)
|
|
try:
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True})
|
|
finally:
|
|
os.close(foreign_fd)
|
|
|
|
assert response.status_code == 503, response.text
|
|
body = response.json()
|
|
assert body["code"] == "settings_locked"
|
|
assert body["saved"] is False
|
|
assert not onboarding.settings_path.exists()
|
|
assert onboarding.calls["env"] == []
|
|
assert onboarding.calls["supervisor"] == 0
|
|
assert lock_path.exists() # the other holder's lock was neither taken nor removed
|
|
|
|
|
|
def test_the_precondition_never_runs_without_the_lock(monkeypatch, tmp_path):
|
|
"""The same class, at the seam every owner endpoint shares."""
|
|
import os
|
|
|
|
import ouroboros.config as config
|
|
from ouroboros.gateway.owner_settings import SettingsLockUnavailable, _owner_write_settings
|
|
|
|
settings_path = tmp_path / "settings.json"
|
|
monkeypatch.setattr(config, "DATA_DIR", tmp_path)
|
|
monkeypatch.setattr(config, "SETTINGS_PATH", settings_path)
|
|
checked: list = []
|
|
foreign_fd = os.open(str(tmp_path / "settings.json.lock"),
|
|
os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o644)
|
|
try:
|
|
with pytest.raises(SettingsLockUnavailable):
|
|
_owner_write_settings({"TOTAL_BUDGET": 10.0},
|
|
precondition=lambda: checked.append(1) or "")
|
|
finally:
|
|
os.close(foreign_fd)
|
|
|
|
assert checked == []
|
|
assert not settings_path.exists()
|
|
|
|
|
|
def test_a_post_commit_failure_reports_the_save_that_landed(monkeypatch, onboarding):
|
|
"""FINDING 4: the commit boundary. The bytes are on disk before the
|
|
supervisor starts, so a supervisor failure is its own fact — reporting
|
|
`saved=False` sent the owner back through a completed onboarding."""
|
|
import ouroboros.gateway.settings as gw_settings
|
|
|
|
def _boom(_request, _settings):
|
|
raise RuntimeError("supervisor refused to start")
|
|
|
|
monkeypatch.setattr(gw_settings, "_start_supervisor_if_needed_for_request", _boom)
|
|
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True})
|
|
|
|
assert response.status_code == 500, response.text
|
|
body = response.json()
|
|
assert body["saved"] is True
|
|
assert body["status"] == "saved_with_post_commit_error"
|
|
assert body["post_commit_failed"] == "supervisor start"
|
|
assert "supervisor refused to start" in body["error"]
|
|
# And the transaction really IS on disk, preset marker included.
|
|
saved = onboarding.saved()
|
|
assert saved[PRESET_MARKER_KEY] == "3"
|
|
assert saved[ONBOARDING_COMPLETED_KEY]
|
|
assert saved["OPENROUTER_API_KEY"] == WIZARD_PAYLOAD["OPENROUTER_API_KEY"]
|
|
|
|
|
|
def test_the_preset_save_does_not_stall_on_its_own_lock(onboarding):
|
|
"""FINDING 5: the precondition re-read used to take the settings lock a
|
|
second time. It is not re-entrant, so every successful preset save burned
|
|
the full 2s timeout before reading the file it was already holding."""
|
|
import time
|
|
|
|
started = time.monotonic()
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True})
|
|
elapsed = time.monotonic() - started
|
|
|
|
assert response.status_code == 200, response.text
|
|
assert response.json()["preset"]["applied"] is True
|
|
assert elapsed < 1.0, f"the preset save waited {elapsed:.2f}s on a lock it held"
|
|
|
|
|
|
def test_a_desktop_shaped_completion_still_authors_light_on_a_fresh_install(onboarding):
|
|
"""D-8 closure proof. The desktop `save_wizard` bridge existed for exactly
|
|
ONE reason: a genuinely fresh install had to author the new-install `light`
|
|
safety coverage, and neither the shared validator nor the generic settings
|
|
endpoint may lower safety. Removing that bridge is only honest if the same
|
|
completion the desktop window now posts still lands `light`."""
|
|
assert not onboarding.settings_path.exists() # genuinely fresh
|
|
|
|
response = onboarding.client.post("/api/onboarding/complete", json=dict(WIZARD_PAYLOAD))
|
|
|
|
assert response.status_code == 200, response.text
|
|
assert onboarding.saved()["OUROBOROS_SAFETY_MODE"] == "light"
|
|
|
|
|
|
def test_a_completion_over_an_existing_install_can_never_lower_safety(onboarding):
|
|
"""The other half: the ratchet. With a settings file already on disk the
|
|
install is not fresh, so completion authors nothing — and a payload that
|
|
tries to carry the lowering itself gets no further, because
|
|
OUROBOROS_SAFETY_MODE is not a wizard field at all."""
|
|
onboarding.settings_path.write_text(
|
|
json.dumps({"OUROBOROS_SAFETY_MODE": "full", "OPENROUTER_API_KEY": "sk-or-v1-existing"}),
|
|
encoding="utf-8")
|
|
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "OUROBOROS_SAFETY_MODE": "light"})
|
|
|
|
assert response.status_code == 200, response.text
|
|
assert onboarding.saved()["OUROBOROS_SAFETY_MODE"] == "full"
|
|
|
|
|
|
def test_old_install_is_not_retro_presetted(onboarding):
|
|
"""An install that already has a provider is past install time — the missing
|
|
marker is NOT permission to preset it (every pre-preset install lacks one)."""
|
|
onboarding.settings_path.write_text(json.dumps({
|
|
"OPENROUTER_API_KEY": "sk-or-v1-existing-install-key",
|
|
"OUROBOROS_MODEL": "openai/gpt-5.6-luna",
|
|
"OUROBOROS_SAFETY_MODE": "full",
|
|
}), encoding="utf-8")
|
|
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True},
|
|
)
|
|
|
|
assert response.status_code == 200, response.text
|
|
assert response.json()["preset"]["applied"] is False
|
|
assert response.json()["preset"]["reason"] == "not_install_time"
|
|
assert onboarding.calls["snapshot"] == 0
|
|
saved = onboarding.saved()
|
|
assert not saved.get(PRESET_MARKER_KEY)
|
|
assert not saved.get("OUROBOROS_REVIEWER_SLOTS")
|
|
# The fresh-install safety default is likewise not re-authored over it.
|
|
assert saved["OUROBOROS_SAFETY_MODE"] == "full"
|
|
|
|
|
|
def test_a_presetted_install_is_never_presetted_twice(onboarding):
|
|
first = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True})
|
|
assert first.json()["preset"]["applied"] is True
|
|
|
|
second = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True})
|
|
|
|
assert second.status_code == 200, second.text
|
|
assert second.json()["preset"]["reason"] == "not_install_time"
|
|
assert onboarding.calls["snapshot"] == 1 # still only the FIRST read
|
|
|
|
|
|
def test_install_time_status_cannot_be_forged_from_the_payload(onboarding):
|
|
"""A browser boolean is a request, never an authority: neither the marker
|
|
nor a claimed subscription can make a configured install install-time."""
|
|
onboarding.settings_path.write_text(json.dumps({
|
|
"OPENROUTER_API_KEY": "sk-or-v1-existing-install-key",
|
|
"OUROBOROS_MODEL": "openai/gpt-5.6-luna",
|
|
}), encoding="utf-8")
|
|
|
|
response = onboarding.client.post("/api/onboarding/complete", json={
|
|
**WIZARD_PAYLOAD,
|
|
"subscriptionsConnected": True,
|
|
PRESET_MARKER_KEY: "", # try to clear the latch
|
|
"OUROBOROS_SUBSCRIPTION_PRESET_VERSION ": "1",
|
|
"OUROBOROS_REVIEWER_SLOTS": '{"triad": [], "scope": []}',
|
|
"OUROBOROS_SAFETY_MODE": "off",
|
|
})
|
|
|
|
assert response.status_code == 200, response.text
|
|
assert response.json()["preset"]["applied"] is False
|
|
saved = onboarding.saved()
|
|
assert not saved.get(PRESET_MARKER_KEY)
|
|
# Neither the reviewer slots nor safety mode ride through the wizard payload:
|
|
# the shared setup validator only copies the setup contract's own keys.
|
|
assert not saved.get("OUROBOROS_REVIEWER_SLOTS")
|
|
assert saved.get("OUROBOROS_SAFETY_MODE", "") != "off"
|
|
|
|
|
|
def test_subscription_alone_does_not_satisfy_the_launch_gate(onboarding):
|
|
"""D-1: at least one API key or a local model. A subscription amplifies.
|
|
|
|
The shared setup validator is the first gate and refuses with its own
|
|
provider-list wording; nothing is written and the daemon is never asked."""
|
|
payload = {k: v for k, v in WIZARD_PAYLOAD.items() if k != "OPENROUTER_API_KEY"}
|
|
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete", json={**payload, "subscriptionsConnected": True})
|
|
|
|
assert response.status_code == 400, response.text
|
|
assert "local model" in response.json()["error"]
|
|
assert not onboarding.settings_path.exists()
|
|
assert onboarding.calls["snapshot"] == 0
|
|
|
|
|
|
def test_startup_gate_is_re_checked_after_normalization(monkeypatch, onboarding):
|
|
"""Defence in depth on the SAME invariant: even if a payload passed the
|
|
shared validator, an install that would not be startup-ready is refused
|
|
before anything is saved — a subscription never fills that gap."""
|
|
import ouroboros.gateway.onboarding as gw_onboarding
|
|
|
|
monkeypatch.setattr(
|
|
gw_onboarding, "apply_runtime_provider_defaults",
|
|
lambda settings: ({k: ("" if k == "OPENROUTER_API_KEY" else v)
|
|
for k, v in settings.items()}, False, []))
|
|
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete", json={**WIZARD_PAYLOAD, "subscriptionsConnected": True})
|
|
|
|
assert response.status_code == 400, response.text
|
|
assert "API key or a local model" in response.json()["error"]
|
|
assert not onboarding.settings_path.exists()
|
|
assert onboarding.calls["snapshot"] == 0
|
|
|
|
|
|
def test_non_object_body_is_refused(onboarding):
|
|
response = onboarding.client.post("/api/onboarding/complete", json=["nope"])
|
|
|
|
assert response.status_code == 400
|
|
assert not onboarding.settings_path.exists()
|
|
|
|
|
|
def test_running_process_keeps_its_boot_runtime_mode(onboarding):
|
|
"""The pending next-boot mode is persisted; the live env is not elevated —
|
|
the same split the two-write flow had."""
|
|
from ouroboros.config import get_runtime_mode
|
|
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete", json={**WIZARD_PAYLOAD, "OUROBOROS_RUNTIME_MODE": "pro"})
|
|
|
|
assert response.status_code == 200, response.text
|
|
assert response.json()["runtime_mode"] == "pro"
|
|
assert onboarding.saved()["OUROBOROS_RUNTIME_MODE"] == "pro"
|
|
assert onboarding.calls["env"][0]["OUROBOROS_RUNTIME_MODE"] == get_runtime_mode()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Pure helpers (no server).
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _routable_snapshot(accounts, profiles=(), harnesses=None):
|
|
return {
|
|
"daemon": {"state": "running"},
|
|
"harnesses": harnesses if harnesses is not None else [
|
|
{"id": "claude", "status": "ok", "enabled": True, "models": [{"id": "claude-opus-5"}]},
|
|
{"id": "codex", "status": "ok", "enabled": True, "models": [{"id": "gpt-5.6-sol"}]},
|
|
{"id": "cursor", "status": "ok", "enabled": True,
|
|
"models": [{"id": "cursor-grok-4.6-high"}]},
|
|
],
|
|
"profiles": {"harnessAccounts": list(accounts), "profiles": list(profiles)},
|
|
}
|
|
|
|
|
|
def test_only_daemon_routable_accounts_become_discoveries():
|
|
"""The engine's OWN answer decides: `next_up` says which credential an
|
|
unpinned run would take, and only a subscription seat counts."""
|
|
from ouroboros.gateway.onboarding import verified_harness_discoveries
|
|
|
|
snapshot = _routable_snapshot(
|
|
# claude: the live CLI session. codex: the engine would rotate onto a
|
|
# verified subscription profile. cursor: signed in nowhere.
|
|
[_native_account("claude"),
|
|
_profile_account("codex", "koshak"),
|
|
{"harness_id": "cursor", "native_credentials_enabled": True,
|
|
"native_login_detected": False, "identity": None,
|
|
"next_up": {"kind": "none", "reason": "the default credential is not ready"}}],
|
|
[_profile("codex", "koshak")],
|
|
)
|
|
|
|
discoveries, failure = verified_harness_discoveries(snapshot)
|
|
|
|
assert failure is None
|
|
assert [d.harness_id for d in discoveries] == ["claude", "codex"]
|
|
|
|
|
|
def test_an_api_key_profile_is_never_counted_as_a_subscription():
|
|
"""The reviewer's probe: `credential_kind=api_key` with a PASSED
|
|
verification. Presetting it would put agent_session reviewer rows on a lane
|
|
that bills the owner's API key — exactly what D-3 forbids."""
|
|
from ouroboros.gateway.onboarding import subscription_routable_harnesses
|
|
|
|
routable, refused = subscription_routable_harnesses(_routable_snapshot(
|
|
[_profile_account("claude", "byok")],
|
|
[_profile("claude", "byok", kind="api_key", availability="unavailable")],
|
|
))
|
|
|
|
assert routable == {}
|
|
assert "API credential" in refused["claude"]
|
|
|
|
|
|
def test_a_native_api_key_route_is_never_counted_as_a_subscription():
|
|
"""Same class on the OTHER seat: the default account of an API-key-only
|
|
harness is a detected login too — the engine names its route `api_key`."""
|
|
from ouroboros.gateway.onboarding import subscription_routable_harnesses
|
|
|
|
routable, refused = subscription_routable_harnesses(
|
|
_routable_snapshot([_native_account("codex", route="api_key")]))
|
|
|
|
assert routable == {}
|
|
assert "API key" in refused["codex"]
|
|
|
|
|
|
def test_a_disabled_or_unavailable_harness_is_refused_even_when_signed_in():
|
|
"""The other half of the reviewer's probe: the harness row itself. An
|
|
engine that cannot run the harness cannot run a subscription session on it,
|
|
however healthy the account looks."""
|
|
from ouroboros.gateway.onboarding import subscription_routable_harnesses
|
|
|
|
routable, refused = subscription_routable_harnesses(_routable_snapshot(
|
|
[_native_account("claude"), _native_account("codex")],
|
|
harnesses=[
|
|
{"id": "claude", "status": "ok", "enabled": False, "models": [{"id": "claude-opus-5"}]},
|
|
{"id": "codex", "status": "unavailable", "enabled": True,
|
|
"models": [{"id": "gpt-5.6-sol"}]},
|
|
],
|
|
))
|
|
|
|
assert routable == {}
|
|
assert refused == {"claude": "the engine has this harness disabled",
|
|
"codex": "the engine reports it unavailable"}
|
|
|
|
|
|
def test_an_unverified_or_disabled_profile_seat_is_refused():
|
|
from ouroboros.gateway.onboarding import subscription_routable_harnesses
|
|
|
|
unverified = subscription_routable_harnesses(_routable_snapshot(
|
|
[_profile_account("cursor", "koshakcot-ultra")],
|
|
[_profile("cursor", "koshakcot-ultra", availability="unavailable",
|
|
verification="not_run")]))
|
|
disabled = subscription_routable_harnesses(_routable_snapshot(
|
|
[_profile_account("cursor", "sol-validator")],
|
|
[_profile("cursor", "sol-validator", enabled=False)]))
|
|
missing = subscription_routable_harnesses(
|
|
_routable_snapshot([_profile_account("cursor", "ghost")]))
|
|
|
|
assert unverified[0] == {} and "unavailable" in unverified[1]["cursor"]
|
|
assert disabled[0] == {} and "disabled" in disabled[1]["cursor"]
|
|
assert missing[0] == {} and "does not list" in missing[1]["cursor"]
|
|
|
|
|
|
# The engine's own `next_up` refusal strings (Claudexor
|
|
# `orchestrator/credential-profiles.ts::nextUpIdentity`), copied verbatim.
|
|
_NOT_READY_NOW = ("the default credential is not ready; refresh Accounts or run "
|
|
"`claudexor doctor`")
|
|
_ROUTE_UNKNOWN_NOW = ("the default credential route is unknown; refresh Accounts or run "
|
|
"`claudexor doctor`")
|
|
|
|
|
|
def _no_capacity_now(harness, *, native_login):
|
|
"""A harness whose subscription IS configured and whose CURRENT routing answer
|
|
is a refusal — the shape of a spent window at the moment of onboarding."""
|
|
return {"harness_id": harness, "native_credentials_enabled": True,
|
|
"native_login_detected": native_login,
|
|
"identity": {"email": "owner@example.com", "plan": "claude_max"}
|
|
if native_login else None,
|
|
"next_up": {"kind": "none",
|
|
"reason": _NOT_READY_NOW if native_login else _ROUTE_UNKNOWN_NOW}}
|
|
|
|
|
|
def test_a_routing_refusal_now_does_not_delete_a_subscription_from_the_preset():
|
|
"""FINDING 5. `next_up` is the daemon's answer to "who would an unpinned run
|
|
take RIGHT NOW", computed from enabled profiles + default readiness + QUOTA
|
|
(Claudexor INV-135), and the engine documents it as informational — it never
|
|
gates routing. The preset is a once-only install-time decision (D-4) that
|
|
never runs again, so deciding it on that answer meant an owner who connected
|
|
Claude and Codex during an hour when the Claude window happened to be spent
|
|
got a Codex-only preset PERMANENTLY, with no seam left to revisit it. D-3
|
|
says an exhausted subscription row stays CONFIGURED and waits for capacity.
|
|
|
|
Two shapes, both with the engine's verbatim reason strings: a signed-in
|
|
default session the engine will not route this minute, and a harness whose
|
|
only seat is a named subscription profile (the default `limit_action: fail`
|
|
policy never names a profile in `next_up`, so this is the ordinary Cursor
|
|
account shape)."""
|
|
from ouroboros.gateway.onboarding import subscription_routable_harnesses
|
|
|
|
routable, refused = subscription_routable_harnesses(_routable_snapshot(
|
|
[_no_capacity_now("claude", native_login=True),
|
|
_no_capacity_now("cursor", native_login=False)],
|
|
[_profile("cursor", "sol-validator")],
|
|
))
|
|
|
|
assert refused == {}
|
|
assert set(routable) == {"claude", "cursor"}
|
|
# The evidence stays honest: the seat, and the engine's own reason beside it.
|
|
assert routable["claude"] == f"signed-in default session; no capacity right now ({_NOT_READY_NOW})"
|
|
assert routable["cursor"] == (
|
|
f"account 'sol-validator' (config_dir_login); no capacity right now ({_ROUTE_UNKNOWN_NOW})")
|
|
|
|
|
|
def test_a_seat_with_no_capacity_still_reaches_the_compiled_preset_with_its_models():
|
|
"""The same finding one layer up: the harness must not merely be 'routable',
|
|
it must survive into the compiled preset with its models resolved — which is
|
|
what the owner's reviewer and subagent rows are actually written from."""
|
|
from ouroboros.gateway.onboarding import verified_harness_discoveries
|
|
|
|
snapshot = _routable_snapshot(
|
|
[_no_capacity_now("claude", native_login=True), _native_account("codex")],
|
|
harnesses=[
|
|
{"id": "claude", "status": "ok", "enabled": True,
|
|
"models": [{"id": "claude-opus-5"}, {"id": "claude-sonnet-5"},
|
|
{"id": "claude-fable-5"}, {"id": "claude-opus-4-6"}]},
|
|
{"id": "codex", "status": "ok", "enabled": True,
|
|
"models": [{"id": "gpt-5.6-sol"}, {"id": "gpt-5.6-terra"}, {"id": "gpt-5.5"}]},
|
|
],
|
|
)
|
|
|
|
discoveries, failure = verified_harness_discoveries(snapshot)
|
|
|
|
assert failure is None
|
|
assert [d.harness_id for d in discoveries] == ["claude", "codex"]
|
|
assert "claude-opus-5" in dict((d.harness_id, d.model_ids) for d in discoveries)["claude"]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# The UNIFIED account model's wire (sprint plan §K.7 + frozen contract §L):
|
|
# `harnessAccounts` arrives EMPTY (every account a named registry row) and the
|
|
# routing verdict rides the additive `accountPools: [{harness_id, next_up}]`.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _unified_snapshot(pools, profiles=(), harnesses=None, legacy_accounts=()):
|
|
snapshot = _routable_snapshot(list(legacy_accounts), profiles, harnesses)
|
|
snapshot["profiles"]["accountPools"] = list(pools)
|
|
return snapshot
|
|
|
|
|
|
def _pool(harness, next_up):
|
|
return {"harness_id": harness, "next_up": next_up}
|
|
|
|
|
|
def test_a_unified_engine_routes_through_account_pools_not_the_empty_legacy_key():
|
|
"""The dual-read's load-bearing half: on a unified engine the legacy
|
|
`harnessAccounts` is `[]`, so the old accounts.get(harness) authority is
|
|
absent — skipping there would silently drop EVERY unified harness from the
|
|
preset. The pool row is the accounts authority on that wire."""
|
|
from ouroboros.gateway.onboarding import subscription_routable_harnesses
|
|
|
|
routable, refused = subscription_routable_harnesses(_unified_snapshot(
|
|
[_pool("claude", {"kind": "profile", "profileId": "claude-default"}),
|
|
_pool("codex", {"kind": "api_key_route"})],
|
|
[_profile("claude", "claude-default")],
|
|
))
|
|
|
|
assert routable == {"claude": "account 'claude-default' (config_dir_login)"}
|
|
# The pool union's explicit API-key verdict is a refusal, same wording
|
|
# class as the legacy native api_key route.
|
|
assert "API key" in refused["codex"]
|
|
# No pool row and no legacy row: silent absence, exactly as before.
|
|
assert "cursor" not in routable and "cursor" not in refused
|
|
|
|
|
|
def test_a_unified_refusal_still_keeps_a_configured_named_seat_in_the_preset():
|
|
"""D-3 through the new wire: a spent window at onboarding time must not
|
|
delete a configured subscription from the once-only preset. On the unified
|
|
wire the configured-seat fallback is the named-profile scan ALONE — there
|
|
is no native fact to read."""
|
|
from ouroboros.gateway.onboarding import subscription_routable_harnesses
|
|
|
|
routable, refused = subscription_routable_harnesses(_unified_snapshot(
|
|
[_pool("claude", {"kind": "none", "reason": "every window is spent"}),
|
|
_pool("cursor", {"kind": "none", "reason": "nothing routable"})],
|
|
[_profile("claude", "claude-default")],
|
|
))
|
|
|
|
assert routable == {
|
|
"claude": "account 'claude-default' (config_dir_login); "
|
|
"no capacity right now (every window is spent)"}
|
|
assert refused == {"cursor": "nothing routable"}
|
|
|
|
|
|
def test_an_unknown_pool_kind_is_fail_safe_and_the_seat_scan_still_answers():
|
|
"""Either wire may grow a spelling this reader predates (plan §E: degrade
|
|
honestly). An unknown kind is never a subscription verdict; a configured
|
|
named profile still counts through the seat scan, and a harness with none
|
|
is refused with the unknown state named."""
|
|
from ouroboros.gateway.onboarding import subscription_routable_harnesses
|
|
|
|
routable, refused = subscription_routable_harnesses(_unified_snapshot(
|
|
[_pool("claude", {"kind": "quantum_pool"}),
|
|
_pool("codex", {"kind": "quantum_pool"})],
|
|
[_profile("claude", "claude-default")],
|
|
))
|
|
|
|
assert set(routable) == {"claude"}
|
|
assert "unknown routing state 'quantum_pool'" in routable["claude"]
|
|
assert "unknown routing state 'quantum_pool'" in refused["codex"]
|
|
|
|
|
|
def test_when_both_wires_carry_a_verdict_the_pool_wins():
|
|
"""A transitional payload may carry both. Profiles own account facts, the
|
|
pool owns routing facts — one authority, or the two could disagree about
|
|
the same harness."""
|
|
from ouroboros.gateway.onboarding import subscription_routable_harnesses
|
|
|
|
routable, refused = subscription_routable_harnesses(_unified_snapshot(
|
|
[_pool("codex", {"kind": "api_key_route"})],
|
|
legacy_accounts=[_native_account("codex")], # legacy says: healthy session
|
|
))
|
|
|
|
assert routable == {}
|
|
assert "API key" in refused["codex"]
|
|
|
|
|
|
def test_out_of_capacity_never_launders_an_api_key_or_an_unusable_seat():
|
|
"""The round-one finding must not regress through the new fallback: being out
|
|
of capacity is not evidence of being a subscription. A harness whose only
|
|
seats are API credentials, disabled or unverified stays refused."""
|
|
from ouroboros.gateway.onboarding import subscription_routable_harnesses
|
|
|
|
# The default login exists, but the harness's auth_preference puts an API key
|
|
# ahead of it — durable, and exactly what D-3 forbids.
|
|
api_route = subscription_routable_harnesses(
|
|
_routable_snapshot([_native_account("codex", route="api_key")],
|
|
[_profile("codex", "koshak", kind="api_key")]))
|
|
# No native login at all; the only named seats are unusable.
|
|
only_bad_profiles = subscription_routable_harnesses(_routable_snapshot(
|
|
[_profile_account("cursor", "ghost")],
|
|
[_profile("cursor", "byok", kind="api_key"),
|
|
_profile("cursor", "off", enabled=False),
|
|
_profile("cursor", "new", verification="not_run")]))
|
|
|
|
assert api_route[0] == {} and "API key" in api_route[1]["codex"]
|
|
assert only_bad_profiles[0] == {} and "does not list" in only_bad_profiles[1]["cursor"]
|
|
|
|
|
|
def test_no_routable_account_is_a_typed_failure_that_names_the_reason():
|
|
from ouroboros.gateway.onboarding import verified_harness_discoveries
|
|
|
|
discoveries, failure = verified_harness_discoveries(_routable_snapshot(
|
|
[_native_account("claude", detected=False)]))
|
|
|
|
assert discoveries == ()
|
|
assert failure is not None and failure.code == "no_verified_account"
|
|
assert "no signed-in session" in failure.detail
|
|
|
|
|
|
def test_an_engine_with_no_accounts_authority_vouches_nothing():
|
|
from ouroboros.gateway.onboarding import verified_harness_discoveries
|
|
|
|
discoveries, failure = verified_harness_discoveries({
|
|
"daemon": {"state": "running"}, "harnesses": [], "profiles": {}})
|
|
|
|
assert discoveries == ()
|
|
assert failure is not None and failure.code == "no_verified_account"
|
|
|
|
|
|
def test_generic_settings_save_cannot_author_or_clear_preset_facts():
|
|
"""Marker and receipt are endpoint-authored: Settings neither writes nor wipes."""
|
|
from ouroboros.gateway.settings import _merge_settings_payload
|
|
|
|
merged = _merge_settings_payload(
|
|
{PRESET_MARKER_KEY: "1", SUBAGENTS_RECEIPT_KEY: "old", "TOTAL_BUDGET": 10.0},
|
|
{PRESET_MARKER_KEY: "99", SUBAGENTS_RECEIPT_KEY: "new", "TOTAL_BUDGET": 20.0},
|
|
)
|
|
|
|
assert merged[PRESET_MARKER_KEY] == "1"
|
|
assert merged[SUBAGENTS_RECEIPT_KEY] == "old"
|
|
assert merged["TOTAL_BUDGET"] == 20.0
|
|
|
|
fresh = _merge_settings_payload(
|
|
{"TOTAL_BUDGET": 10.0},
|
|
{PRESET_MARKER_KEY: "1", SUBAGENTS_RECEIPT_KEY: "new"},
|
|
)
|
|
assert not fresh.get(PRESET_MARKER_KEY)
|
|
assert not fresh.get(SUBAGENTS_RECEIPT_KEY)
|
|
|
|
|
|
def test_get_onboarding_read_writes_nothing(monkeypatch, tmp_path):
|
|
"""A READ must never be the first author of settings.json (D-8)."""
|
|
import ouroboros.config as config
|
|
import ouroboros.gateway.settings as gw_settings
|
|
|
|
for key in _PROVIDER_ENV:
|
|
monkeypatch.delenv(key, raising=False)
|
|
settings_path = tmp_path / "settings.json"
|
|
monkeypatch.setattr(config, "DATA_DIR", tmp_path)
|
|
monkeypatch.setattr(config, "SETTINGS_PATH", settings_path)
|
|
|
|
app = Starlette(routes=[Route("/api/onboarding", endpoint=gw_settings.api_onboarding)])
|
|
with TestClient(app) as client:
|
|
first = client.get("/api/onboarding")
|
|
|
|
assert first.status_code == 200 # the blocking overlay
|
|
assert not settings_path.exists(), "GET /api/onboarding created settings.json"
|
|
|
|
# And with a settings file present, its bytes and mtime are untouched.
|
|
settings_path.write_text(json.dumps({"OUROBOROS_MODEL": "openai/gpt-5.6-luna"}),
|
|
encoding="utf-8")
|
|
before_bytes = settings_path.read_bytes()
|
|
before_mtime = settings_path.stat().st_mtime_ns
|
|
with TestClient(app) as client:
|
|
second = client.get("/api/onboarding")
|
|
|
|
assert second.status_code == 200
|
|
assert settings_path.read_bytes() == before_bytes
|
|
assert settings_path.stat().st_mtime_ns == before_mtime
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# The freshness seam: completion derives the WHOLE document from an unlocked
|
|
# read and writes that whole dictionary back, so a concurrent owner write
|
|
# landing in between would be reverted key by key while the owner is told the
|
|
# save succeeded. These drive the real endpoint through the real persist path.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_WATCHED = "OUROBOROS_AUTO_GRANT_REVIEWED_SKILLS"
|
|
|
|
|
|
def _write_concurrently(path, **values):
|
|
"""Another owner write, exactly as a second process would leave the file."""
|
|
current = json.loads(path.read_text(encoding="utf-8")) if path.exists() else {}
|
|
current.update(values)
|
|
path.write_text(json.dumps(current, indent=1), encoding="utf-8")
|
|
|
|
|
|
def test_a_concurrent_owner_write_is_refused_and_survives(onboarding, monkeypatch):
|
|
"""The defect this seam exists for: the owner flips a setting elsewhere while
|
|
onboarding is being saved, and the save silently puts it back."""
|
|
import ouroboros.gateway.onboarding as gw_onboarding
|
|
|
|
onboarding.settings_path.write_text(
|
|
json.dumps({"OPENROUTER_API_KEY": "sk-or-v1-existing", _WATCHED: "true"}, indent=1),
|
|
encoding="utf-8")
|
|
|
|
# Land the concurrent write in the window the fingerprint covers: after this
|
|
# request read the document, before it takes the lock.
|
|
real_prepared = gw_onboarding._prepared_settings
|
|
|
|
def _prepared_then_interfere(body):
|
|
result = real_prepared(body)
|
|
_write_concurrently(onboarding.settings_path, **{_WATCHED: "false"})
|
|
return result
|
|
|
|
monkeypatch.setattr(gw_onboarding, "_prepared_settings", _prepared_then_interfere)
|
|
|
|
response = onboarding.client.post("/api/onboarding/complete", json=dict(WIZARD_PAYLOAD))
|
|
|
|
assert response.status_code == 409
|
|
body = response.json()
|
|
assert body["saved"] is False
|
|
assert body["code"] == "onboarding_state_changed"
|
|
# The owner's change is still on disk, and onboarding did NOT complete.
|
|
saved = json.loads(onboarding.settings_path.read_text(encoding="utf-8"))
|
|
assert saved[_WATCHED] == "false"
|
|
assert ONBOARDING_COMPLETED_KEY not in saved
|
|
|
|
|
|
def test_the_advised_retry_actually_succeeds(onboarding, monkeypatch):
|
|
"""The refusal tells the owner to finish again, so that must work — and it
|
|
must carry the concurrent value through rather than reverting it."""
|
|
import ouroboros.gateway.onboarding as gw_onboarding
|
|
|
|
onboarding.settings_path.write_text(
|
|
json.dumps({"OPENROUTER_API_KEY": "sk-or-v1-existing", _WATCHED: "true"}, indent=1),
|
|
encoding="utf-8")
|
|
|
|
real_prepared = gw_onboarding._prepared_settings
|
|
interfered = {"done": False}
|
|
|
|
def _interfere_once(body):
|
|
result = real_prepared(body)
|
|
if not interfered["done"]:
|
|
interfered["done"] = True
|
|
_write_concurrently(onboarding.settings_path, **{_WATCHED: "false"})
|
|
return result
|
|
|
|
monkeypatch.setattr(gw_onboarding, "_prepared_settings", _interfere_once)
|
|
|
|
assert onboarding.client.post(
|
|
"/api/onboarding/complete", json=dict(WIZARD_PAYLOAD)).status_code == 409
|
|
second = onboarding.client.post("/api/onboarding/complete", json=dict(WIZARD_PAYLOAD))
|
|
|
|
assert second.status_code == 200
|
|
saved = json.loads(onboarding.settings_path.read_text(encoding="utf-8"))
|
|
assert saved[_WATCHED] == "false", "the retry must not revert the concurrent change either"
|
|
assert ONBOARDING_COMPLETED_KEY in saved
|
|
|
|
|
|
def test_an_uncontended_save_is_never_refused(onboarding):
|
|
"""The check must not cost the ordinary install anything."""
|
|
onboarding.settings_path.write_text(
|
|
json.dumps({"OPENROUTER_API_KEY": "sk-or-v1-existing"}, indent=1), encoding="utf-8")
|
|
|
|
response = onboarding.client.post("/api/onboarding/complete", json=dict(WIZARD_PAYLOAD))
|
|
|
|
assert response.status_code == 200
|
|
assert ONBOARDING_COMPLETED_KEY in json.loads(
|
|
onboarding.settings_path.read_text(encoding="utf-8"))
|
|
|
|
|
|
def test_an_unreadable_settings_file_can_never_compare_equal(onboarding, monkeypatch):
|
|
"""Fail-OPEN corner, found by the delta review of this very seam: folding
|
|
every read failure of one exception class into one stable token let a swap
|
|
between two DIFFERENT unreadable files satisfy the equality check.
|
|
|
|
Reachable, because the loader silently falls back to defaults when it cannot
|
|
read while the atomic rename still lands (the directory stays writable).
|
|
|
|
The refusal is injected rather than provoked with chmod 0o000: on Windows
|
|
chmod only toggles the read-only bit, so the file stays readable and the
|
|
unreadable branch never runs — this failed the first full matrix on
|
|
windows-latest while staying green everywhere it had been run before.
|
|
"""
|
|
import ouroboros.config as cfg
|
|
import ouroboros.gateway.onboarding as gw_onboarding
|
|
|
|
class _Unreadable:
|
|
def read_bytes(self):
|
|
raise PermissionError("injected: settings unreadable")
|
|
|
|
monkeypatch.setattr(cfg, "SETTINGS_PATH", _Unreadable(), raising=False)
|
|
first = gw_onboarding._settings_fingerprint()
|
|
second = gw_onboarding._settings_fingerprint()
|
|
|
|
assert first.startswith("unreadable:") and second.startswith("unreadable:")
|
|
assert first != second, "an unreadable file must refuse, never satisfy equality"
|
|
|
|
|
|
def test_a_connected_agy_account_composes_with_core_reviewers(onboarding):
|
|
# The REAL agy wire shape (Claudexor INV-135): a harness with no default
|
|
# credential store reports its harness ROW "unavailable" structurally and
|
|
# its next_up as kind="none" (the default credential is never ready), so
|
|
# the routable verdict comes from the CONFIGURED-profile fallback over the
|
|
# verified named profile. The old fixture's status:"ok" native agy account
|
|
# cannot occur.
|
|
onboarding.calls["snapshot_payload"] = {
|
|
"daemon": {"state": "running"},
|
|
"harnesses": [
|
|
{"id": "claude", "status": "ok", "enabled": True,
|
|
"models": [{"id": "claude-opus-5"}, {"id": "claude-sonnet-5"},
|
|
{"id": "claude-fable-5"}, {"id": "claude-opus-4-6"}]},
|
|
{"id": "agy", "status": "unavailable", "enabled": True,
|
|
"models": [{"id": "gemini-3.7-flash-high"}, {"id": "gemini-3.1-pro-high"}]},
|
|
],
|
|
"profiles": {
|
|
# next_up kind="none": the engine returns none whenever the
|
|
# DEFAULT credential is not ready, which is agy's permanent state —
|
|
# the routable verdict must come from the configured-profile
|
|
# fallback, not from a profile-kind next_up.
|
|
"harnessAccounts": [
|
|
_native_account("claude"),
|
|
{"harness_id": "agy", "native_credentials_enabled": True,
|
|
"native_login_detected": False, "identity": None,
|
|
"next_up": {"kind": "none", "reason": "no default credential store"}},
|
|
],
|
|
"profiles": [_profile("agy", "work")],
|
|
},
|
|
}
|
|
|
|
response = onboarding.client.post(
|
|
"/api/onboarding/complete",
|
|
json={**WIZARD_PAYLOAD, "subscriptionsConnected": True},
|
|
)
|
|
|
|
assert response.status_code == 200, response.text
|
|
saved = onboarding.saved()
|
|
actor_targets = [
|
|
row["route"]["target_id"]
|
|
for row in json.loads(saved[SUBAGENTS_SETTING])["items"]
|
|
]
|
|
assert "agy=gemini-3.7-flash-high" in actor_targets
|
|
reviewer = json.loads(saved["OUROBOROS_REVIEWER_SLOTS"])
|
|
roster = {
|
|
row["subagent_id"]: row["route"]["target_id"]
|
|
for row in json.loads(saved[SUBAGENTS_SETTING])["items"]
|
|
}
|
|
triad_targets = [
|
|
roster[row["subagent_id"]] if "subagent_id" in row
|
|
else row["route"]["target_id"]
|
|
for row in reviewer["triad"]
|
|
]
|
|
assert all("agy=" not in target for target in triad_targets)
|
|
assert onboarding.calls["supervisor"] == 1
|
|
|
|
|
|
def test_an_unavailable_harness_row_with_a_verified_named_profile_is_routable():
|
|
# The runnability proof for a named profile is its own doctor probe, not
|
|
# the harness row: agy's row is STRUCTURALLY "unavailable" (no default
|
|
# credential store, Claudexor INV-135), and refusing on the row alone made
|
|
# the Agy task-actor policy unreachable on the real wire shape. A
|
|
# native default seat still requires a runnable row (its only signal).
|
|
from ouroboros.gateway.onboarding import subscription_routable_harnesses
|
|
|
|
snapshot = {
|
|
"harnesses": [{"id": "agy", "status": "unavailable", "enabled": True}],
|
|
"profiles": {
|
|
"harnessAccounts": [_profile_account("agy", "work")],
|
|
"profiles": [_profile("agy", "work")],
|
|
},
|
|
}
|
|
routable, refused = subscription_routable_harnesses(snapshot)
|
|
assert "agy" in routable, refused
|
|
assert "no default credential store" in routable["agy"]
|
|
|
|
# Same row, but only a signed-in DEFAULT session: still refused — the
|
|
# harness row is that seat's only runnability signal.
|
|
snapshot = {
|
|
"harnesses": [{"id": "agy", "status": "unavailable", "enabled": True}],
|
|
"profiles": {"harnessAccounts": [_native_account("agy")], "profiles": []},
|
|
}
|
|
routable, refused = subscription_routable_harnesses(snapshot)
|
|
assert "agy" not in routable
|
|
assert refused["agy"] == "the engine reports it unavailable"
|