ouroboros/tests/test_settings_env_on_disk.py
Ouroboros a44815215f v7next F1: domain D12 - settings vocabulary split from tip bytes, launcher seam half landed
Module side: config.py (1598) hands its vocabulary to the five oracle leaves,
re-emitted from TIP bytes with the transplant proof green on every span:
settings_defaults (361; SHARED leaf emitted FULL from BOTH parents per the
ledger - config.py rows 840-854 + provider_models.py rows 3238-3241, final
--check against the concatenated parents), settings_scales (111), model_slots
(113), review_model_routes (131), runtime_limits (192); the facade shrinks
1598 -> 883 with the oracle's re-export import block. The drift-probe
falsified the reference as a copy source on 7 spans (SETTINGS_DEFAULTS,
RETIRED_SETTING_KEYS, ENDPOINT_AUTHORED_SETTINGS, OPENROUTER_REVIEW_DEFAULTS,
get_websearch_timeout_sec, get_search_code_wall_sec, get_max_subagent_depth)
plus one structural reshape: upstream's tuple statement also binds the
unrowed MAX_SUBAGENT_DEPTH_HARD_CAP, which rides into runtime_limits and is
re-exported (F5 row needed; the tool's undeclared-top-level gate has a
Tuple-target blind spot - span proofs green, documented). provider_models.py
is touched by span removal + the re-export import ONLY (D02 module). The
reference's D04 retirement of the SOFT/HARD timeout knobs is
upstream-diverged and NOT replayed.

launcher_onboarding.py lands the launcher HALF of the approved D03 settings
seam (rows 918-920): zero upstream drift since merge-base, reference bytes
verbatim - startup stops persisting the pre-server provider normalization;
the server-side mirror stays for the D11 lane. The config.py in-place seam
rows 914/916 (normalize_settings_raw / serialize_settings) are HOT-DEFERRED:
upstream rewrote the read path through the new settings_integrity module, so
a verbatim replay would revert it; their pin test_settings_read_seam.py
defers with the machinery.

Test side: test_config_extraction.py + test_settings_env_on_disk.py
transplanted (adaptations: the tuple-bound cap in the owner inventory, one
literal re-pinned to tip's ENDPOINT_AUTHORED_SETTINGS, the provider_models
clause narrowed to this tree - the no-config-import half returns with D02);
the onboarding pins follow the launcher delta (row 920 rename, wizard and
server_runtime launcher clauses; every server clause keeps tip bytes).
Lossless: 19==19 / 63==63 / 28==28 test functions on the touched suites, one
ledger-mandated rename. Two path-keyed mirrors updated (prompt-cache TTL
definition-sites, hotreload key-paths set).

CI-shape battery on the final tree: parallel 11807 tests 0 failed (3
skipped) rc=0; serial 618 tests 0 failed (9 skipped) rc=0; -m size_ratchet 5
passed (official manifest regeneration = byte no-op); ruff --select F clean;
HEAD held through every pytest run. Ledger corrections: entries 13-19 in
docs/v7next/LEDGER_CORRECTIONS.md.

(cherry picked from commit 3a6a0344eabb9b933352c5fc32cabb0a79b186e5)
2026-08-30 18:38:38 +00:00

207 lines
9.5 KiB
Python

"""Which environment values may become file content, and which never may.
Settings travel in both directions: `apply_settings_to_env` projects the document
into `os.environ` so subprocesses inherit it, and `load_settings` overlays the
environment onto keys the file does not mention. That overlay is what lets a
benchmark or an operator forward a value for one run without editing anyone's
settings; it is also how a forwarded value can end up PERSISTED as an owner
decision by an unrelated save. Owner decision (spec 4.3.7, answer A): the current
split stands, and these tests are the record of it.
The split has three parts:
1. **Alias keys are never written.** A key that exists only as backwards
compatibility for the environment (`OUROBOROS_MODEL_FALLBACK`, singular) is
read where it is read and is not part of the settings vocabulary, so no save
can put it on disk under either name.
2. **A canonical env value may be pinned by an EXPLICIT write.** `load_settings`
returns it, and a caller that deliberately saves what it loaded persists it.
That is the owner's escape hatch and stays available.
3. **...except for the keys that are disk-authored, where silence stays silence.**
`_DISK_AUTHORED_SETTINGS` (the two context-mode keys and the safety mode) and
`ENDPOINT_AUTHORED_SETTINGS` (the install-time facts) are ratchet or provenance
surfaces: an environment value there is not an owner decision, so it is neither
read into the document nor projected back out of one the file does not carry.
`_settings_file_value` reads DISK ONLY for the same reason — a ratchet whose
"previous value" came from the environment would let any subprocess open the
gate by exporting the value it wants to move away from.
"""
from __future__ import annotations
import json
import os
import pytest
@pytest.fixture
def isolated_settings(tmp_path, monkeypatch):
from ouroboros import config as cfg
data_dir = tmp_path / "data"
data_dir.mkdir()
settings_path = data_dir / "settings.json"
monkeypatch.setattr(cfg, "DATA_DIR", data_dir, raising=True)
monkeypatch.setattr(cfg, "SETTINGS_PATH", settings_path, raising=True)
for key in cfg.SETTINGS_DEFAULTS:
monkeypatch.delenv(key, raising=False)
monkeypatch.delenv("OUROBOROS_MODEL_FALLBACK", raising=False)
cfg.reset_runtime_mode_baseline_for_tests()
yield settings_path
cfg.reset_runtime_mode_baseline_for_tests()
def test_the_singular_fallback_alias_is_read_from_env_and_never_reaches_disk(
isolated_settings, monkeypatch):
"""The env-only alias: a live benchmark contract on the read side, invisible on
the write side. It is not in the settings vocabulary, so no save can persist it,
and it does not silently become a value for the canonical plural key either."""
from ouroboros import config as cfg
monkeypatch.setenv("OUROBOROS_MODEL_FALLBACK", "bench/only-chain")
assert "OUROBOROS_MODEL_FALLBACK" not in cfg.SETTINGS_DEFAULTS
assert "OUROBOROS_MODEL_FALLBACK" not in cfg.settings_env_keys()
assert cfg.parse_fallback_chain() == ["bench/only-chain"], "the read-side alias is live"
loaded = cfg.load_settings()
assert "OUROBOROS_MODEL_FALLBACK" not in loaded
assert loaded["OUROBOROS_MODEL_FALLBACKS"] == (
cfg.SETTINGS_DEFAULTS["OUROBOROS_MODEL_FALLBACKS"]), "the alias leaked into the slot"
cfg.save_settings(loaded)
stored = json.loads(isolated_settings.read_text(encoding="utf-8"))
assert "OUROBOROS_MODEL_FALLBACK" not in stored
def test_a_forwarded_canonical_value_is_read_and_can_be_pinned_by_an_explicit_write(
isolated_settings, monkeypatch):
"""The owner's escape hatch, and the reason a forwarded value is not simply
ignored: it applies for the run, and a deliberate save makes it durable."""
from ouroboros import config as cfg
monkeypatch.setenv("OUROBOROS_MODEL", "forwarded/main")
monkeypatch.setenv("OUROBOROS_MAX_ROUNDS", "17")
loaded = cfg.load_settings()
assert loaded["OUROBOROS_MODEL"] == "forwarded/main"
assert loaded["OUROBOROS_MAX_ROUNDS"] == 17
assert not isolated_settings.exists(), "reading a forwarded value pinned it"
cfg.save_settings(loaded)
stored = json.loads(isolated_settings.read_text(encoding="utf-8"))
assert stored["OUROBOROS_MODEL"] == "forwarded/main"
assert stored["OUROBOROS_MAX_ROUNDS"] == 17
def test_a_stored_value_wins_over_the_environment_for_an_ordinary_key(
isolated_settings, monkeypatch):
"""The overlay fills SILENCE, it does not override the owner's file."""
from ouroboros import config as cfg
isolated_settings.write_text(json.dumps({"OUROBOROS_MODEL": "owner/choice"}), encoding="utf-8")
monkeypatch.setenv("OUROBOROS_MODEL", "forwarded/main")
assert cfg.load_settings()["OUROBOROS_MODEL"] == "owner/choice"
@pytest.mark.parametrize("key,env_value", [
("OUROBOROS_CONTEXT_MODE", "low"),
("OUROBOROS_CONTEXT_MODE_AUTO_LOW", "false"),
("OUROBOROS_SAFETY_MODE", "off"),
])
def test_a_disk_authored_key_is_never_read_out_of_the_environment(
isolated_settings, monkeypatch, key, env_value):
"""These three are ratchets. An environment value is not authorship, so the
document never picks one up — otherwise an ordinary load/save round-trip in a
process whose environment says low/off would launder that value onto disk."""
from ouroboros import config as cfg
assert key in cfg._DISK_AUTHORED_SETTINGS
monkeypatch.setenv(key, env_value)
loaded = cfg.load_settings()
assert loaded[key] == cfg.SETTINGS_DEFAULTS[key], "an env ratchet value reached the document"
cfg.save_settings(loaded)
stored = json.loads(isolated_settings.read_text(encoding="utf-8"))
assert key not in stored, "silence did not stay silence"
def test_a_disk_authored_key_is_not_projected_back_out_of_a_silent_file(
isolated_settings, monkeypatch):
"""The mirror direction: projecting a default the file never carried would
clobber a legitimately forwarded value (a benchmark runner has no settings.json
at all), so an unauthored key is left exactly as the environment has it."""
from ouroboros import config as cfg
monkeypatch.setenv("OUROBOROS_CONTEXT_MODE", "low")
monkeypatch.setenv("OUROBOROS_SAFETY_MODE", "off")
cfg.apply_settings_to_env(dict(cfg.SETTINGS_DEFAULTS))
assert os.environ["OUROBOROS_CONTEXT_MODE"] == "low"
assert os.environ["OUROBOROS_SAFETY_MODE"] == "off"
# ...and once the FILE carries the key, the file is the authority again.
isolated_settings.write_text(json.dumps({"OUROBOROS_CONTEXT_MODE": "max"}), encoding="utf-8")
cfg.apply_settings_to_env(dict(cfg.SETTINGS_DEFAULTS, OUROBOROS_CONTEXT_MODE="max"))
assert os.environ["OUROBOROS_CONTEXT_MODE"] == "max"
def test_install_time_facts_are_disk_only_in_both_directions(isolated_settings, monkeypatch):
"""`ENDPOINT_AUTHORED_SETTINGS` is stricter than the ratchets: those project
once the file carries them, these never leave disk at all. An environment
timestamp alone once closed the onboarding window on a fresh install."""
from ouroboros import config as cfg
for key in cfg.ENDPOINT_AUTHORED_SETTINGS:
monkeypatch.setenv(key, "2020-01-01T00:00:00Z")
assert key not in cfg.settings_env_keys()
loaded = cfg.load_settings()
for key in cfg.ENDPOINT_AUTHORED_SETTINGS:
assert loaded[key] == cfg.SETTINGS_DEFAULTS[key]
isolated_settings.write_text(
json.dumps({key: "2026-01-01T00:00:00Z" for key in cfg.ENDPOINT_AUTHORED_SETTINGS}),
encoding="utf-8")
cfg.apply_settings_to_env(cfg.load_settings())
for key in cfg.ENDPOINT_AUTHORED_SETTINGS:
assert os.environ[key] == "2020-01-01T00:00:00Z", "a disk-only fact was projected"
def test_the_ratchet_previous_value_is_read_from_disk_only(isolated_settings, monkeypatch):
"""`_settings_file_value` is the ratchet's memory. Reading the environment there
would turn ``max -> low`` into ``low -> low`` and open the gate for any process
that can export a variable."""
from ouroboros import config as cfg
isolated_settings.write_text(json.dumps({"OUROBOROS_CONTEXT_MODE": "max"}), encoding="utf-8")
monkeypatch.setenv("OUROBOROS_CONTEXT_MODE", "low")
assert cfg._settings_file_value("OUROBOROS_CONTEXT_MODE", "max") == "max"
with pytest.raises(PermissionError, match="OUROBOROS_CONTEXT_MODE lowering refused"):
cfg.save_settings({"OUROBOROS_CONTEXT_MODE": "low"})
# A key the file does not carry answers the fail-closed default, never the env.
monkeypatch.setenv("OUROBOROS_SAFETY_MODE", "off")
assert cfg._settings_file_value("OUROBOROS_SAFETY_MODE", "full") == "full"
def test_the_exemption_sets_are_exactly_the_declared_ones():
"""A structural pin so the two exemptions cannot grow or shrink unnoticed: each
is a decision about who may author a value, not a convenience list."""
from ouroboros import config as cfg
assert cfg._DISK_AUTHORED_SETTINGS == (
"OUROBOROS_CONTEXT_MODE", "OUROBOROS_CONTEXT_MODE_AUTO_LOW", "OUROBOROS_SAFETY_MODE")
assert cfg.ENDPOINT_AUTHORED_SETTINGS == frozenset(
{"OUROBOROS_SUBSCRIPTION_PRESET_VERSION", "OUROBOROS_SUBAGENT_PRESET_RECEIPT",
"OUROBOROS_ONBOARDING_COMPLETED_AT"})
assert cfg.ENDPOINT_AUTHORED_SETTINGS <= cfg.SETTINGS_KEYS_NOT_EXPORTED_TO_ENV
# The exported set is DERIVED, never hand-kept: a new key exports by default and
# an exclusion is a decision written into the one list.
assert set(cfg.settings_env_keys()) == (
set(cfg.SETTINGS_DEFAULTS) - set(cfg.SETTINGS_KEYS_NOT_EXPORTED_TO_ENV))