ouroboros/tests/test_model_slot_role_model.py
Ouroboros 9dd3c4ee30 Remove the semantic duplicate-task gate from subagent admission (#884)
The supervisor asked a light model whether every new `schedule_subagent`
child was "materially the same" as any active task and rejected it as
`rejected_duplicate` on a positive answer. The judge never saw the selected
subagent, model, lane or executor, so it rejected an intentional cross-model
check (one objective sent to two configured subagents). In 46 days of live
logs it adjudicated 147 spawns, rejected one (that false positive) and never
caught a real duplicate; the only real duplicate in the window was a root
task the gate cannot see. Each call ran synchronously on the supervisor main
loop (2.4 s median, 20 s max), and a BudgetExceeded inside it dropped the
whole schedule event.

Capability change, owner-accepted 2026-09-14: admission no longer vetoes a
child for resembling another task. Exact task-id fencing, the per-root
active-child cap, depth caps and cost ceilings remain the floor; the parent
decides what to spawn and already receives task_id, objective and
subagent_id for every child. Residual risk, disclosed: a parent re-queued
after a mid-wave crash may pay a wave twice, bounded by the cap and the
per-tree ceiling.

- supervisor/events_schedule_task.py: drop `_find_duplicate_task`,
  `_format_task_for_dedup` and the gate call; keep the cap logic.
- supervisor/events.py: drop the re-export.
- tests: drop the judge's unit tests and the 26 monkeypatch stubs that
  silenced it; add a regression test that identical siblings with
  different (and with identical) configured subagents are both admitted.
- The `rejected_duplicate` status constant and its projections stay so old
  task records and history replay keep reading correctly.
- docs/ARCHITECTURE.md: the module row no longer claims a duplicate gate;
  docs/CHECKLISTS_ARCHIVE.md: standing disclosure for item 21.

Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
2026-09-14 17:36:39 +03:00

1725 lines
82 KiB
Python

"""F1 (v6.39): model-slot role-model + 429-aware fallback chain + cooldown.
Covers the empty->Main accessors, the new comma-separated fallback chain
(dedup / drop-active / benchmark no-op / legacy-singular env), the stored-key
rename migration, the process-local cooldown, and the subagent lane resolver
(mutating-child -> heavy, read-only -> light, explicit honored, depth-cap note).
"""
from __future__ import annotations
import json
import os
import pathlib
import pytest
import ouroboros.config as config
from ouroboros import fallback_cooldown as fcd
from ouroboros import subagents
@pytest.fixture(autouse=True)
def _owned_gateway_uses_each_test_transport(monkeypatch):
from ouroboros import claudexor_daemon
class _Gateway:
def close(self):
pass
monkeypatch.setattr(
claudexor_daemon,
"ensure_owned_gateway",
_Gateway,
)
# ---------------------------------------------------------------- accessors
def test_heavy_and_light_empty_fall_back_to_main(monkeypatch):
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main-x")
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "")
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", "")
assert config.get_heavy_model() == "provider::main-x"
assert config.get_light_model() == "provider::main-x"
def test_heavy_and_light_explicit_values_are_honored(monkeypatch):
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main-x")
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "provider::strong")
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", "provider::cheap")
assert config.get_heavy_model() == "provider::strong"
assert config.get_light_model() == "provider::cheap"
# ----------------------------------------------------------- fallback chain
def test_fallback_chain_dedups_and_drops_active(monkeypatch):
monkeypatch.setenv("OUROBOROS_MODEL_FALLBACKS", "a, b , a, c")
monkeypatch.delenv("OUROBOROS_MODEL_FALLBACK", raising=False)
assert config.get_fallback_models("b") == ["a", "c"]
# No active model -> full deduped chain in order.
assert config.get_fallback_models("") == ["a", "b", "c"]
def test_fallback_chain_benchmark_dedupes_to_no_op(monkeypatch):
# Benchmark sets every slot to one model; the active model is dropped, so the
# chain collapses to empty -> no cross-model fallback happens.
monkeypatch.setenv("OUROBOROS_MODEL_FALLBACKS", "same::model")
monkeypatch.delenv("OUROBOROS_MODEL_FALLBACK", raising=False)
assert config.get_fallback_models("same::model") == []
def test_fallback_chain_reads_legacy_singular_env(monkeypatch):
monkeypatch.delenv("OUROBOROS_MODEL_FALLBACKS", raising=False)
monkeypatch.setenv("OUROBOROS_MODEL_FALLBACK", "legacy::single")
assert config.get_fallback_models("primary") == ["legacy::single"]
def test_fallback_chain_empty_means_no_fallback(monkeypatch):
# An explicitly empty/unset Fallbacks slot must NOT silently fall back to the shipped
# Anthropic default (which would cross an OpenAI-compatible/local owner into an
# unconfigured provider). The default reaches a default install via the env instead.
monkeypatch.delenv("OUROBOROS_MODEL_FALLBACKS", raising=False)
monkeypatch.delenv("OUROBOROS_MODEL_FALLBACK", raising=False)
assert config.get_fallback_models("primary") == []
def test_advisory_fallback_model_uses_main_when_light_empty(monkeypatch):
from ouroboros.tools.claude_advisory_review import _resolve_fallback_model
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main-x")
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", "")
# Empty Light must resolve to Main, never "" (which would call chat with no model id).
assert _resolve_fallback_model() == "provider::main-x"
def test_parse_fallback_chain_ssot(monkeypatch):
monkeypatch.setenv("OUROBOROS_MODEL_FALLBACKS", "a, b , a")
monkeypatch.delenv("OUROBOROS_MODEL_FALLBACK", raising=False)
# Raw chain: parsed, whitespace-trimmed, NO dedup, NO active-drop (those belong to
# get_fallback_models on top).
assert config.parse_fallback_chain() == ["a", "b", "a"]
monkeypatch.delenv("OUROBOROS_MODEL_FALLBACKS", raising=False)
monkeypatch.setenv("OUROBOROS_MODEL_FALLBACK", "legacy")
assert config.parse_fallback_chain() == ["legacy"]
def test_infer_model_category_recognizes_chain_link(monkeypatch):
from ouroboros.pricing import infer_model_category
monkeypatch.setenv("OUROBOROS_MODEL", "main/x")
monkeypatch.delenv("OUROBOROS_MODEL_HEAVY", raising=False)
monkeypatch.delenv("OUROBOROS_MODEL_LIGHT", raising=False)
monkeypatch.setenv("OUROBOROS_MODEL_FALLBACKS", "fb/one, fb/two")
monkeypatch.delenv("OUROBOROS_MODEL_FALLBACK", raising=False)
# A model that is a LINK of the chain is categorized "fallback", not "other".
assert infer_model_category("fb/two") == "fallback"
assert infer_model_category("main/x") == "main"
assert infer_model_category("unrelated/z") == "other"
# -------------------------------------------------------- stored migration
def test_stored_slot_keys_migrate_on_load(monkeypatch, tmp_path):
settings_file = tmp_path / "settings.json"
settings_file.write_text(json.dumps({
"OUROBOROS_MODEL": "provider::main",
"OUROBOROS_MODEL_CODE": "provider::legacy-code",
"USE_LOCAL_CODE": True,
"OUROBOROS_MODEL_FALLBACK": "provider::legacy-fb",
}), encoding="utf-8")
monkeypatch.setattr(config, "SETTINGS_PATH", pathlib.Path(settings_file))
for key in ("OUROBOROS_MODEL_HEAVY", "USE_LOCAL_HEAVY", "OUROBOROS_MODEL_FALLBACKS",
"OUROBOROS_MODEL_CODE", "USE_LOCAL_CODE", "OUROBOROS_MODEL_FALLBACK"):
monkeypatch.delenv(key, raising=False)
loaded = config.load_settings()
assert loaded.get("OUROBOROS_MODEL_HEAVY") == "provider::legacy-code"
assert loaded.get("USE_LOCAL_HEAVY") is True
assert loaded.get("OUROBOROS_MODEL_FALLBACKS") == "provider::legacy-fb"
# Legacy keys are dropped, not left to linger.
assert "OUROBOROS_MODEL_CODE" not in loaded
assert "USE_LOCAL_CODE" not in loaded
assert "OUROBOROS_MODEL_FALLBACK" not in loaded
def test_migrate_legacy_slot_keys_ssot():
# The shared SSOT helper preserves a stored value, drops the legacy key, and never
# clobbers an already-set new key.
s = {"OUROBOROS_MODEL_CODE": "x", "USE_LOCAL_CODE": True, "OUROBOROS_MODEL_FALLBACK": "y"}
config.migrate_legacy_slot_keys(s)
assert s == {"OUROBOROS_MODEL_HEAVY": "x", "USE_LOCAL_HEAVY": True, "OUROBOROS_MODEL_FALLBACKS": "y"}
# An already-set new key wins; the legacy key is still dropped.
s2 = {"OUROBOROS_MODEL_CODE": "old", "OUROBOROS_MODEL_HEAVY": "new"}
config.migrate_legacy_slot_keys(s2)
assert s2 == {"OUROBOROS_MODEL_HEAVY": "new"}
def test_colab_settings_migrate_legacy_drive_keys():
# A Colab re-run with legacy Drive settings.json must keep the owner's prior
# code/heavy + fallback customizations (not silently drop them).
from ouroboros.colab_bootstrap import build_colab_settings
existing = {
"OUROBOROS_MODEL": "openai::gpt-5.5",
"OUROBOROS_MODEL_CODE": "openai::gpt-5.5-custom-heavy",
"OUROBOROS_MODEL_FALLBACK": "openai::gpt-5.5-mini",
"OPENAI_API_KEY": "sk-openai-existing",
}
out = build_colab_settings({}, models=None, existing=existing)
assert out.get("OUROBOROS_MODEL_HEAVY") == "openai::gpt-5.5-custom-heavy"
assert out.get("OUROBOROS_MODEL_FALLBACKS") == "openai::gpt-5.5-mini"
assert "OUROBOROS_MODEL_CODE" not in out
assert "OUROBOROS_MODEL_FALLBACK" not in out
# ---------------------------------------------------------------- cooldown
def test_cooldown_marks_and_heals(monkeypatch):
fcd.reset_for_tests()
monkeypatch.delenv("OUROBOROS_FALLBACK_COOLDOWN_ENABLED", raising=False)
monkeypatch.setenv("OUROBOROS_FALLBACK_COOLDOWN_SEC", "120")
assert fcd.is_cooling_down("m1") is False
fcd.mark_cooldown("m1")
assert fcd.is_cooling_down("m1") is True
# A zero-length window heals immediately on the next read (passive heal).
monkeypatch.setenv("OUROBOROS_FALLBACK_COOLDOWN_SEC", "0")
fcd.mark_cooldown("m2")
assert fcd.is_cooling_down("m2") is False
def test_cooldown_disabled_is_noop(monkeypatch):
fcd.reset_for_tests()
monkeypatch.setenv("OUROBOROS_FALLBACK_COOLDOWN_ENABLED", "false")
fcd.mark_cooldown("m1")
assert fcd.is_cooling_down("m1") is False
def test_cooldown_local_and_remote_are_distinct(monkeypatch):
fcd.reset_for_tests()
monkeypatch.delenv("OUROBOROS_FALLBACK_COOLDOWN_ENABLED", raising=False)
monkeypatch.setenv("OUROBOROS_FALLBACK_COOLDOWN_SEC", "120")
fcd.mark_cooldown("m1", use_local=True)
assert fcd.is_cooling_down("m1", use_local=True) is True
assert fcd.is_cooling_down("m1", use_local=False) is False
def test_attempts_per_model_is_bounded(monkeypatch):
monkeypatch.setenv("OUROBOROS_FALLBACK_ATTEMPTS_PER_MODEL", "9")
assert fcd.attempts_per_model() == 2
monkeypatch.setenv("OUROBOROS_FALLBACK_ATTEMPTS_PER_MODEL", "0")
assert fcd.attempts_per_model() == 1
monkeypatch.setenv("OUROBOROS_FALLBACK_ATTEMPTS_PER_MODEL", "nonsense")
assert fcd.attempts_per_model() == 1
# ------------------------------------------------------------ lane resolver
def test_omitted_lane_inherits_the_parents_lane(monkeypatch):
"""An omitted lane INHERITS the parent's effective lane (v6.87.26).
It used to collapse to `light` whatever the parent was running, so a Heavy
parent handing a child a slice of its own job got a Light child and no signal.
Cheap work is still Light — but the parent has to SAY so, which is the whole
point: the declaration is visible instead of implied by silence. The child's
write authority still does not enter the choice (the v6.87.7 decoupling)."""
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "provider::strong")
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", "provider::cheap")
res = subagents.resolve_subagent_lane("auto", parent_lane="heavy")
assert res.effective_lane == "heavy"
assert res.model == "provider::strong"
# A parent that judges the work cheap names it, and is obeyed.
named = subagents.resolve_subagent_lane("light", parent_lane="heavy")
assert named.effective_lane == "light"
assert named.model == "provider::cheap"
# No lane on record is the root agent, and the root runs Main.
root_child = subagents.resolve_subagent_lane("auto")
assert root_child.effective_lane == "main"
assert root_child.model == "provider::main"
def test_resolver_takes_no_authority_argument():
"""The resolver must not regrow an authority input. If a future change adds one,
this fails and the reviewer sees the coupling coming back."""
import inspect
params = set(inspect.signature(subagents.resolve_subagent_lane).parameters)
assert "mutating" not in params
assert not (params & {"mutating", "may_mutate", "write_surface", "surface"})
assert "requested_lane" in params
# Deliberately NOT an equality assertion: what this test is FOR is the absence of
# an authority input, and pinning the exact signature would mean a future cleanup
# has to delete a test in order to delete dead code (v6.87.28 deleted the
# slot_index/slot_count fan-out parameters exactly that way).
def test_explicit_lane_is_honored_at_any_depth(monkeypatch):
"""Depth bounds how DEEP delegation goes, never how strong a descendant is —
structurally: depth is not a resolver input at all (the dead parameter was
removed in XG-2R.4; the signature guard below watches for regrowth)."""
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "provider::strong")
assert "depth" not in __import__("inspect").signature(subagents.resolve_subagent_lane).parameters
res = subagents.resolve_subagent_lane("heavy")
assert res.effective_lane == "heavy"
assert res.model == "provider::strong"
def test_explicit_main_honored(monkeypatch):
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
res = subagents.resolve_subagent_lane("main")
assert res.effective_lane == "main"
def test_code_lane_is_rejected_no_legacy_alias():
with pytest.raises(ValueError):
subagents.normalize_subagent_model_lane("code")
def test_build_envelope_tolerates_legacy_stored_lane():
# The PUBLIC schema rejects "code", but an envelope built from an already-ran task's
# durable record (which may carry a pre-v6.39 "code" lane) must NOT crash — it coerces
# the unknown stored lane to a safe default (not a "code"->"heavy" alias).
env = subagents.build_subagent_envelope(
task_id="t1", parent_task_id="p1", root_task_id="r1", task_group_id="",
depth=1, role="builder", requested_lane="code", effective_lane="code",
model="m", status="completed", usage={},
)
assert env["requested_lane"] == "auto"
# The coercion target is LANE_OF_RECORD — the same answer the resolver gives for
# "no lane on record" — not a second hardcoded default that outlives the first.
assert env["effective_lane"] == subagents.LANE_OF_RECORD == "main"
def test_string_false_may_mutate_stays_falsey(monkeypatch):
# A tool-call payload may carry may_mutate as the STRING "false"; the SSOT
# normalize_bool must treat it as falsey (regression: bool("false") was truthy).
# Since v6.87.7 may_mutate governs AUTHORITY only — it no longer reaches the lane
# resolver at all — so this pins the primitive rather than a routing side effect.
from ouroboros.contracts.task_contract import normalize_bool
assert normalize_bool("false") is False
assert normalize_bool("true") is True
def test_use_local_empty_heavy_follows_main_flag(monkeypatch):
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "")
monkeypatch.setenv("USE_LOCAL_MAIN", "true")
monkeypatch.delenv("USE_LOCAL_HEAVY", raising=False)
res = subagents.resolve_subagent_lane("heavy")
# The lane REPORTS the slot the model actually came from: with no Heavy slot
# configured this child runs Main, and calling it "heavy" claimed a strength
# nobody configured (v6.87.26 — the reduction capability_delta announces).
assert res.effective_lane == "main"
assert res.model == "provider::main"
# Empty Heavy -> Main, so the Main local flag governs (not silently ignored).
assert res.use_local_model is True
# ------------------------------------------------- cooldown trigger SSOT (C1)
def test_cooldown_error_kinds_include_rate_limit_but_not_in_retry_kinds():
from ouroboros.loop_llm_call import _COOLDOWN_ERROR_KINDS, _TRANSIENT_RETRY_KINDS
# A body-error 429 is classified "rate_limit" -> it MUST trigger cooldown.
assert "rate_limit" in _COOLDOWN_ERROR_KINDS
assert _TRANSIENT_RETRY_KINDS <= _COOLDOWN_ERROR_KINDS
# ...but the same-model transient-retry budget must NOT be widened by it.
assert "rate_limit" not in _TRANSIENT_RETRY_KINDS
# ------------------------------------ credentialed-model resolver parses chain (C2)
def test_resolve_credentialed_model_parses_fallbacks_chain(monkeypatch):
from ouroboros.provider_models import resolve_credentialed_model
# Only OpenRouter is credentialed in this environment.
monkeypatch.setenv("OPENROUTER_API_KEY", "or-key")
for k in ("GIGACHAT_CREDENTIALS", "GIGACHAT_USER", "GIGACHAT_PASSWORD",
"OPENAI_API_KEY", "ANTHROPIC_API_KEY", "CLOUDRU_FOUNDATION_MODELS_API_KEY"):
monkeypatch.delenv(k, raising=False)
monkeypatch.setenv("OUROBOROS_MODEL", "gigachat::GigaChat") # uncredentialed
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "")
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", "")
# First chain entry uncredentialed (gigachat), second routes via OpenRouter.
monkeypatch.setenv("OUROBOROS_MODEL_FALLBACKS", "gigachat::nocreds, anthropic/claude-sonnet-4.6")
# The resolver must parse the chain and return the credentialed SECOND entry — not
# test the raw comma-string as one (broken) model id, nor skip past it.
assert resolve_credentialed_model("gigachat::GigaChat") == "anthropic/claude-sonnet-4.6"
def test_empty_light_slot_inherits_main_routing_even_when_models_match(monkeypatch):
"""ENV PRESENCE decides the inherit-from-Main case, not string equality. A
local-only install whose Main happens to equal the shipped Light default must
still route the Light lane locally — it is running Main, not the Light slot."""
from ouroboros.config import SETTINGS_DEFAULTS
from ouroboros.subagents import _use_local_for_lane
shared = SETTINGS_DEFAULTS["OUROBOROS_MODEL_LIGHT"]
monkeypatch.setenv("OUROBOROS_MODEL", shared)
monkeypatch.delenv("OUROBOROS_MODEL_LIGHT", raising=False)
monkeypatch.setenv("USE_LOCAL_MAIN", "true")
monkeypatch.delenv("USE_LOCAL_LIGHT", raising=False)
assert _use_local_for_lane("light", shared) is True
# A slot the owner really configured still governs itself.
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", shared)
assert _use_local_for_lane("light", shared) is False
def _scheduling_ctx(tmp_path, *, parent_deadline: str = "", parent_lane: str = ""):
import queue
from ouroboros.tools.registry import ToolContext
ctx = ToolContext(repo_dir=tmp_path, drive_root=tmp_path)
ctx.task_id = "parent1"
ctx.task_depth = 0
ctx.current_chat_id = 1
ctx.event_queue = queue.Queue()
ctx.active_model = "provider::parent"
ctx.active_effort = "high"
ctx.active_use_local = False
ctx.task_metadata = {"root_task_id": "root1", "session_id": "sess1"}
if parent_lane:
ctx.task_metadata["effective_model_lane"] = parent_lane
if parent_deadline:
ctx.task_metadata["task_contract"] = {"deadline_at": parent_deadline}
return ctx
def test_subagent_id_replaces_the_public_executor_axis(tmp_path, monkeypatch):
"""The selected row, not a second public executor argument, chooses substrate."""
from ouroboros.tools.control import _schedule_task
from tests._shared import configure_test_subagent
api_id = configure_test_subagent(monkeypatch, subagent_id="api", kind="api_model")
api_ctx = _scheduling_ctx(tmp_path / "api")
assert "TOOL_ARG_ERROR" not in _schedule_task(
api_ctx, subagent_id=api_id, objective="o", expected_output="e",
)
assert api_ctx.event_queue.get_nowait()["requested_executor"] == "native"
session_id = configure_test_subagent(
monkeypatch, subagent_id="session", kind="agent_session",
target="claude=claude-fable-5",
)
session_ctx = _scheduling_ctx(tmp_path / "session")
assert "TOOL_ARG_ERROR" not in _schedule_task(
session_ctx, subagent_id=session_id, objective="o", expected_output="e",
)
assert session_ctx.event_queue.get_nowait()["requested_executor"] == "harness"
conflict = _schedule_task(
_scheduling_ctx(tmp_path / "conflict"), subagent_id=session_id,
objective="o", expected_output="e", executor="harness",
)
assert "subagent_selector_conflict" in conflict
def test_effort_is_not_an_owner_facing_axis(tmp_path, monkeypatch):
"""There are THREE owner-facing axes and effort is not one of them (v6.87.28).
A parent declares the WORK: write_surface (what the child may do), model_lane
(how good the answer must be), executor (where it runs). A public `effort` broke
that twice — it was a second knob for the question `model_lane` already answers,
so `model_lane=light` with `effort=max` pinned the cheapest model to the
strongest reasoning with no rule to reconcile them; and a harness route carries
its own effort, so a parent asking `low` against a route pinned to `xhigh` had no
rule for who wins. The refusal names the withdrawal instead of calling a
parameter that was real for four releases 'unsupported'."""
from ouroboros.tools.control import _schedule_task, schedule_subagent_properties
from tests._shared import configure_test_subagent
subagent_id = configure_test_subagent(monkeypatch)
assert "effort" not in schedule_subagent_properties()
ctx = _scheduling_ctx(tmp_path / "named")
out = _schedule_task(ctx, objective="o", expected_output="e", effort="xhigh")
assert "TOOL_ARG_ERROR" in out and "effort was withdrawn" in out
assert "model_lane" in out
assert ctx.event_queue.empty()
# The combination that had no answer is refused at the door, not ranked.
ctx = _scheduling_ctx(tmp_path / "conflict")
out = _schedule_task(ctx, objective="o", expected_output="e",
model_lane="light", effort="max")
assert "TOOL_ARG_ERROR" in out
assert ctx.event_queue.empty()
# Scheduling states intent; nothing about effort is recorded there at all.
ctx = _scheduling_ctx(tmp_path / "omitted")
assert "TOOL_ARG_ERROR" not in _schedule_task(
ctx, subagent_id=subagent_id, objective="o", expected_output="e",
)
assert "reasoning_effort" not in ctx.event_queue.get_nowait()
def test_effort_is_derived_from_the_owner_setting_at_dispatch(tmp_path, monkeypatch):
"""Removing the knob did not remove the capability: the owner still controls
effort through `config.resolve_effort(task_type)`, exactly as they did whenever
the parameter was omitted — which was the normal case."""
from ouroboros.agent import resolve_dispatch_axes
monkeypatch.setenv("OUROBOROS_EFFORT_TASK", "xhigh")
task = {"id": "c1", "type": "task", "delegation_role": "subagent"}
dispatch = resolve_dispatch_axes(task)
assert dispatch.effort == "xhigh"
assert task["reasoning_effort"] == "xhigh"
assert task["capability_delta"]["derived_effort"] == "xhigh"
monkeypatch.setenv("OUROBOROS_EFFORT_TASK", "low")
assert resolve_dispatch_axes({"id": "c2", "type": "task",
"delegation_role": "subagent"}).effort == "low"
def test_a_stored_legacy_effort_is_ignored_with_the_reason_stated(tmp_path):
"""`effort` was model-visible and sits on durable records written before it was
withdrawn. Loading one must not crash, must not obey it, and must not drop it in
silence — a value that quietly stops meaning anything is the same class of defect
as a reduction nobody announces."""
from ouroboros.agent import capability_delta_prompt_block, resolve_dispatch_axes
from ouroboros.subagents import LEGACY_SUBAGENT_FIELDS
assert "reasoning_effort" in LEGACY_SUBAGENT_FIELDS
task = {"id": "c1", "type": "task", "delegation_role": "subagent",
"reasoning_effort": "max"}
dispatch = resolve_dispatch_axes(task)
# Not obeyed: the derived effort wins, whatever the record said.
assert dispatch.effort == config.resolve_effort("task") != "max"
assert task["reasoning_effort"] == dispatch.effort
# ...and not dropped in silence.
note = task["capability_delta"]["legacy_note"]
assert "reasoning_effort='max'" in note and "derived" in note
assert "Ignored on your record" in capability_delta_prompt_block(dispatch)
# An ignored field is not a REDUCTION — nothing was taken away.
assert task["capability_delta"]["reduced"] is False
# A stray `effort` inside a stored task contract is dropped by the contract
# builder rather than raising: contracts outlive the schema that wrote them.
from ouroboros.contracts.task_contract import build_task_contract
contract = build_task_contract({"id": "c1", "task_contract": {"effort": "max"}})
assert "effort" not in contract
def _enqueue_through_supervisor(tmp_path, monkeypatch, *, parent_lane: str = "", **schedule_kwargs):
"""Drive the REAL path: tool call -> event -> supervisor -> the task a worker is handed."""
from types import SimpleNamespace
from supervisor import events as ev_module
from ouroboros.tools.control import _schedule_task
from tests._shared import configure_test_subagent
ctx = _scheduling_ctx(tmp_path, parent_lane=parent_lane)
legacy_executor = str(schedule_kwargs.pop("executor", "") or "").strip()
legacy_lane = str(schedule_kwargs.pop("model_lane", "") or "").strip()
actor_effort = str(schedule_kwargs.pop("_actor_effort", "high") or "")
if not schedule_kwargs.get("subagent_id"):
is_session = legacy_executor == "harness"
if is_session:
target = "claude=route-a"
monkeypatch.setattr(
"ouroboros.subagents.route_health",
lambda *_args, **_kwargs: ("configured_session_route_unavailable", ""),
)
else:
monkeypatch.setattr(
"ouroboros.provider_models.model_has_credentials", lambda _model: True,
)
if legacy_lane == "light":
target = str(config.get_light_model() or config.get_main_model())
elif legacy_lane == "heavy":
target = str(config.get_heavy_model() or config.get_main_model())
else:
target = str(os.environ.get("OUROBOROS_MODEL") or "provider::main")
schedule_kwargs["subagent_id"] = configure_test_subagent(
monkeypatch,
subagent_id="session-actor" if is_session else "api-actor",
kind="agent_session" if is_session else "api_model",
target=target,
effort=actor_effort,
)
out = _schedule_task(ctx, objective="o", expected_output="e", **schedule_kwargs)
assert "TOOL_ARG_ERROR" not in out, out
event = ctx.event_queue.get_nowait()
event["type"] = "schedule_subagent"
event["depth"] = 0
event["delegation_role"] = ""
enqueued = []
class FakeCtx:
DRIVE_ROOT = tmp_path
PENDING = []
RUNNING = {}
WORKERS = {0: SimpleNamespace(busy_task_id=None)}
def load_state(self):
return {"owner_chat_id": 1}
def send_with_budget(self, chat_id, text, **kwargs):
pass
def enqueue_task(self, task):
enqueued.append(task)
def persist_queue_snapshot(self, reason=""):
pass
ev_module._handle_schedule_task(event, FakeCtx())
assert enqueued, "supervisor did not enqueue the task"
return enqueued[0]
def test_the_request_reaches_the_worker_and_only_the_request(tmp_path, monkeypatch):
"""The parent's INTENT must reach the task the WORKER is handed — and nothing else.
This asserts on the task the supervisor actually enqueues, not on the event and not on
a re-implementation of the agent's fallback. An earlier version of this test built the
payload itself from the event, which meant it supplied the very keys under test and
could not fail — and a version before THAT re-implemented the agent's three lines in
the test body. Both passed while the supervisor was silently dropping the keys on the
floor. The loss is destructive, not merely inert: the worker writes its own view back
over the durable record, so a drop here also erases the evidence of what was asked.
The second half is the v6.87.28 invariant: what the child GETS is not on this task,
because it has not been resolved. A schedule-time answer about live availability is
an answer about a moment that has passed by the time the child starts."""
task = _enqueue_through_supervisor(
tmp_path, monkeypatch, parent_lane="heavy", executor="harness")
assert task["requested_executor"] == "harness"
assert task["requested_model_lane"] == "auto"
assert task["parent_model_lane"] == "heavy"
assert task["metadata"]["requested_executor"] == "harness"
assert task["metadata"]["parent_model_lane"] == "heavy"
for derived in ("effective_model_lane", "model", "use_local_model",
"reasoning_effort", "effective_executor", "capability_delta"):
assert derived not in task, derived
assert derived not in task["metadata"], derived
def test_availability_is_a_dispatch_fact_not_a_schedule_fact(tmp_path, monkeypatch):
"""The reason there is exactly one resolution and it runs at dispatch.
A child scheduled while no harness route exists can wait out the whole outage in
the queue. Resolving at schedule time froze the answer onto the record forever;
resolving again at dispatch produced a SECOND record that disagreed with the first
about the same child. With the D28 correction the down state is a typed BLOCK, so
freezing it at schedule time would have refused a child whose route came back
while it sat in the queue."""
from ouroboros.agent import resolve_dispatch_axes
import ouroboros.subagents as subagent_module
task = _enqueue_through_supervisor(tmp_path, monkeypatch, executor="harness")
state = {"reason": "subscription_window_exhausted"}
monkeypatch.setattr(
subagent_module, "route_health",
lambda *_args, **_kwargs: (state["reason"], "2030-01-01T00:00:00Z"),
)
down = resolve_dispatch_axes(dict(task))
assert (down.executor, down.route) == ("blocked", "")
assert down.blocked is True and down.delta.reduced is True
# The exact configured route comes back while the same immutable task waits.
state["reason"] = ""
up = resolve_dispatch_axes(dict(task))
assert (up.executor, up.route) == ("harness", "claude=route-a")
assert up.delta.reduced is False
def test_deadline_at_narrows_but_never_extends(tmp_path, monkeypatch):
"""`deadline_at` is public as of v6.87.7, and narrowing-only: a child may be bound
tighter than its parent, never looser."""
from ouroboros.tools.control import _INTERNAL_SCHEDULE_OPTIONS, _schedule_task
from tests._shared import configure_test_subagent
subagent_id = configure_test_subagent(monkeypatch)
assert _INTERNAL_SCHEDULE_OPTIONS == frozenset()
# Relative to now, not hardcoded: `deadline_at` must be a FUTURE instant, so fixed
# calendar dates in this test would silently turn into rejections as time passes.
from datetime import timedelta
from ouroboros.deadline_utils import utc_now
def stamp(hours):
return (utc_now() + timedelta(hours=hours)).strftime("%Y-%m-%dT%H:%M:%SZ")
parent, tighter, looser = stamp(12), stamp(9), stamp(23)
ctx = _scheduling_ctx(tmp_path / "tighter", parent_deadline=parent)
_schedule_task(
ctx, subagent_id=subagent_id, objective="o", expected_output="e",
deadline_at=tighter,
)
evt = ctx.event_queue.get_nowait()
assert evt["task_contract"]["deadline_at"] == tighter
ctx = _scheduling_ctx(tmp_path / "looser", parent_deadline=parent)
_schedule_task(
ctx, subagent_id=subagent_id, objective="o", expected_output="e",
deadline_at=looser,
)
evt = ctx.event_queue.get_nowait()
assert evt["task_contract"]["deadline_at"] == parent
# A model-authored deadline is validated, because both failures are otherwise silent.
ctx = _scheduling_ctx(tmp_path / "garbage")
out = _schedule_task(
ctx, subagent_id=subagent_id, objective="o", expected_output="e",
deadline_at="in 2 hours",
)
assert "TOOL_ARG_ERROR" in out and "ISO-8601" in out
assert ctx.event_queue.empty()
ctx = _scheduling_ctx(tmp_path / "past")
out = _schedule_task(
ctx, subagent_id=subagent_id, objective="o", expected_output="e",
deadline_at=stamp(-1),
)
assert "TOOL_ARG_ERROR" in out and "already in the past" in out
assert ctx.event_queue.empty()
def test_the_envelope_states_the_request_until_dispatch_fills_it_in(tmp_path, monkeypatch):
"""The envelope is the subagent's public description, and until the child is
dispatched the honest description has an intent and NO answer. `effective_lane`
used to default to `light`, so a queued child's envelope named a lane, a slot and
a strength that no resolution had produced — a claim, not a record."""
from ouroboros.agent import resolve_dispatch_axes
from ouroboros.tools.control import _schedule_task
from tests._shared import configure_test_subagent
ctx = _scheduling_ctx(tmp_path / "asked")
subagent_id = configure_test_subagent(
monkeypatch, subagent_id="session-actor", kind="agent_session",
target="claude=route-a",
)
_schedule_task(
ctx, subagent_id=subagent_id, objective="o", expected_output="e",
)
envelope = ctx.event_queue.get_nowait()["subagent_envelope"]
assert envelope["executor"] == "harness" # the request
assert envelope["effective_lane"] == "" # nothing resolved yet
assert envelope["reasoning_effort"] == ""
assert envelope["effective_executor"] == ""
assert envelope["capability_delta"] == {}
task = _enqueue_through_supervisor(tmp_path / "ran", monkeypatch, executor="harness")
resolve_dispatch_axes(task)
filled = task["subagent_envelope"]
assert filled["effective_lane"] == "main"
assert filled["model"] and filled["reasoning_effort"]
# The pin no route can honor is a typed block, never a silent re-route to paid
# native execution (D28).
assert filled["effective_executor"] == "blocked"
assert filled["tool_profile"] == "local_readonly_subagent"
assert filled["capability_delta"]["reduced"] is True
def test_the_scheduling_intent_survives_a_queue_snapshot(tmp_path, monkeypatch):
"""A pending child that waits through a restart must come back holding what its
parent asked for, INCLUDING the parent's own lane: an omitted lane inherits it and
only the parent knew it, so a resumed child without it would resolve `auto`
against the lane of record and silently come back weaker. The intent lives at the
task TOP LEVEL because that is where the resolution reads it — restoring only the
copies nested in `metadata` would leave a resumed child resolving from nothing."""
import supervisor.queue as q
task = _enqueue_through_supervisor(
tmp_path, monkeypatch, parent_lane="heavy", executor="harness")
import json as _json
captured = {}
monkeypatch.setattr(q, "atomic_write_text",
lambda path, text: captured.update(_json.loads(text)))
monkeypatch.setattr(q, "PENDING", [task], raising=False)
monkeypatch.setattr(q, "RUNNING", {}, raising=False)
assert q.persist_queue_snapshot(reason="test") is True
rows = captured.get("pending") or []
assert rows, captured
restored = rows[0]["task"]
assert restored["requested_executor"] == "harness"
assert restored["parent_model_lane"] == "heavy"
def test_a_dispatched_childs_delta_survives_a_restart(tmp_path, monkeypatch):
"""The other half: once a child HAS been dispatched, its resolution must not be
re-derived by a replay. A RUNNING row that came back through a snapshot without
the delta would leave the child believing its pin had been honored.
This pins the SERIALIZATION half only (the snapshot's field list) by injecting
an already-resolved task into RUNNING; how the resolution REACHES the
supervisor's RUNNING copy across the process boundary is pinned by
test_the_workers_resolution_crosses_the_process_boundary_to_the_snapshot —
without that merge, this test alone passed while real snapshots stayed
unresolved (XG-2R.1)."""
import supervisor.queue as q
from ouroboros.agent import resolve_dispatch_axes
task = _enqueue_through_supervisor(tmp_path, monkeypatch, executor="harness")
resolve_dispatch_axes(task)
import json as _json
captured = {}
monkeypatch.setattr(q, "atomic_write_text",
lambda path, text: captured.update(_json.loads(text)))
monkeypatch.setattr(q, "PENDING", [], raising=False)
monkeypatch.setattr(q, "RUNNING", {"c1": {"task": task, "worker_id": 0,
"started_at": 0.0, "attempt": 1}}, raising=False)
assert q.persist_queue_snapshot(reason="test") is True
restored = (captured.get("running") or [])[0]["task"]
assert restored["effective_executor"] == "blocked"
assert restored["capability_delta"]["reduced"] is True
assert restored["reasoning_effort"] == "high"
def test_the_workers_resolution_crosses_the_process_boundary_to_the_snapshot(tmp_path, monkeypatch):
"""XG-2R.1 (three reviewers converged): `resolve_dispatch_axes` stamps the WORKER
process's clone of the task, `assign_tasks` holds its own `dict(task)` in RUNNING,
and `persist_queue_snapshot` serializes the supervisor's copy — so without a
worker->supervisor merge the real snapshot carried the UNRESOLVED intent and a
restart lost the resolved axes and `capability_delta`.
This test crosses the REAL seam instead of hand-injecting a resolved task:
the worker's copy is a serialized clone (as pickling across the process
boundary makes it), the resolution travels ONLY as the JSON-serializable
`task_dispatch_resolved` event through the REAL registered handler
(`dispatch_event`), the handler itself takes the snapshot, and the restored
row must carry the resolved axes + delta."""
import json as _json
import queue as queue_mod
import supervisor.queue as q
from supervisor import events as ev_module
from ouroboros.agent import emit_dispatch_resolution, resolve_dispatch_axes
from ouroboros.subagents import SUBAGENT_RESOLUTION_FIELDS
supervisor_task = _enqueue_through_supervisor(tmp_path, monkeypatch, executor="harness")
# The supervisor's RUNNING copy at assignment — intent only, exactly as
# assign_tasks stores it BEFORE the worker resolves anything.
running = {"c1": {"task": dict(supervisor_task), "worker_id": 0,
"started_at": 0.0, "attempt": 1}}
# The worker receives a SERIALIZED CLONE: its mutations cannot alias into the
# supervisor's dict.
worker_task = _json.loads(_json.dumps(supervisor_task))
worker_task["id"] = "c1"
out_q = queue_mod.Queue()
dispatch = resolve_dispatch_axes(worker_task)
# The merge set is pinned to the one writer: record_fields() + the envelope.
assert set(SUBAGENT_RESOLUTION_FIELDS) == set(dispatch.record_fields()) | {"subagent_envelope"}
emit_dispatch_resolution(out_q, worker_task, dispatch)
# The masked defect, stated: the supervisor's copy is still unresolved.
assert "effective_executor" not in running["c1"]["task"]
captured = {}
monkeypatch.setattr(q, "atomic_write_text",
lambda path, text: captured.update(_json.loads(text)))
monkeypatch.setattr(q, "PENDING", [], raising=False)
monkeypatch.setattr(q, "RUNNING", running, raising=False)
class Ctx:
RUNNING = running
@staticmethod
def persist_queue_snapshot(reason=""):
return q.persist_queue_snapshot(reason=reason)
# Only the event crosses — a JSON round-trip proves nothing shared rides along.
evt = _json.loads(_json.dumps(out_q.get_nowait()))
assert evt["type"] == "task_dispatch_resolved"
ev_module.dispatch_event(evt, Ctx())
# Restart: what a restore reads back is the snapshot the HANDLER persisted.
rows = captured.get("running") or []
assert rows and rows[0]["id"] == "c1", captured.get("reason")
restored = rows[0]["task"]
assert restored["effective_executor"] == "blocked"
assert restored["capability_delta"]["reduced"] is True
assert restored["effective_model_lane"] == "main"
assert restored["model"]
assert restored["reasoning_effort"] == "high"
assert restored["subagent_envelope"]["effective_executor"] == "blocked"
# Intent was merged INTO, not replaced: the request the parent stated survives.
assert restored["requested_executor"] == "harness"
def test_a_prior_resolutions_residue_is_not_a_legacy_request(tmp_path):
"""Consequence of the resolution surviving the snapshot (XG-2R.1, fable's
self_consistency half): a crash-requeued child's record now carries
`reasoning_effort` BECAUSE record_fields() wrote it. Re-dispatching that record
must not disclose a false 'reasoning_effort=... ignored' legacy note to the
child prompt and parent readback — LEGACY_SUBAGENT_FIELDS names fields from
RETIRED SCHEMAS, and a record carrying its own capability_delta proves the
value is the resolver's residue. A genuinely legacy record (no delta) keeps
the note."""
from ouroboros.agent import resolve_dispatch_axes
task = {"id": "c1", "type": "task", "delegation_role": "subagent"}
first = resolve_dispatch_axes(task)
assert first.delta.legacy_note == ""
assert task["reasoning_effort"] # the residue the snapshot now preserves
# The requeue replay: same record, resolution already on it.
replay = resolve_dispatch_axes(dict(task))
assert replay.legacy_ignored == {}
assert replay.delta.legacy_note == ""
# The genuine legacy case is unchanged: stored effort, no prior resolution.
legacy = resolve_dispatch_axes({"id": "c2", "type": "task",
"delegation_role": "subagent",
"reasoning_effort": "max"})
assert "reasoning_effort" in legacy.delta.legacy_note
# ------------------------------------------------------- capability_delta (v6.87.26)
def _light_lane_ctx(tmp_path, monkeypatch, **kwargs):
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", "provider::cheap")
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "provider::strong")
return _scheduling_ctx(tmp_path, **kwargs)
def _dispatched(tmp_path, monkeypatch, **schedule_kwargs):
"""Drive the WHOLE path: tool call -> event -> supervisor -> the worker's dispatch.
Everything a child GETS is decided in the last step, so a test that stops at the
event asserts on intent and calls it a resolution."""
from ouroboros.agent import resolve_dispatch_axes
task = _enqueue_through_supervisor(tmp_path, monkeypatch, **schedule_kwargs)
return task, resolve_dispatch_axes(task)
def test_configured_api_actor_uses_its_exact_model_not_the_legacy_parent_lane(tmp_path, monkeypatch):
"""The stable row is now the model authority; parent lanes are history only."""
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "provider::strong")
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", "provider::cheap")
task, _ = _dispatched(tmp_path / "main", monkeypatch, parent_lane="heavy")
assert task["effective_model_lane"] == "main"
assert task["model"] == "provider::main"
assert task["requested_model_lane"] == "auto"
assert task["capability_delta"]["reduced"] is False
named, _ = _dispatched(tmp_path / "named", monkeypatch, model_lane="light")
assert named["effective_model_lane"] == "main"
assert named["model"] == "provider::cheap"
def test_a_reduction_reaches_the_record_the_child_and_the_parents_readback(tmp_path, monkeypatch):
"""The invariant: a child landing below what was asked for is LOUD in all THREE
places named by the owner — the durable record/envelope, the child's own prompt,
and the TERMINAL parent-facing result.
The executor pin is the reduction that had no reporting at all — `harness` was
recorded on the event, the task and the envelope (under the key `executor`, which
reads as who RAN it) and then no code ever resolved it, so a child that ran
natively left a durable record claiming a harness had run it.
Since the D28 correction the unhonored EXPLICIT pin resolves to `blocked` rather
than to paid `native` (see test_an_explicit_harness_pin_is_a_typed_blocker), so
what the three surfaces must carry is the BLOCK. The disclosure duty is the same;
only the honest answer changed.
None of the three can be reached at SCHEDULE time any more, which is the point:
all three read a fact that does not exist until the child starts."""
from ouroboros.agent import capability_delta_prompt_block
from ouroboros.task_results import write_task_result
from ouroboros.tools.control import _get_task_result
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
task, dispatch = _dispatched(tmp_path / "three", monkeypatch, executor="harness")
# 1) the durable record + its envelope
assert task["effective_executor"] == "blocked"
delta = task["capability_delta"]
assert delta["reduced"] is True
assert delta["reason"] == "configured_session_route_unavailable"
assert task["subagent_envelope"]["capability_delta"] == delta
assert task["subagent_envelope"]["executor"] == "harness"
# 2) the child's own prompt
block = capability_delta_prompt_block(dispatch)
assert "[CAPABILITY DELTA]" in block
assert "executor harness->blocked" in block
# 3) the parent, when it READS the answer
ctx = _scheduling_ctx(tmp_path / "readback")
write_task_result(tmp_path / "readback", "child1", "completed",
result="done", capability_delta=delta)
out = _get_task_result(ctx, "child1")
assert "capability_delta" in out and "configured_session_route_unavailable" in out
# ...and the scheduling result no longer pretends to know: it states the request.
from ouroboros.tools.control import _schedule_task
from tests._shared import configure_test_subagent
sched_ctx = _scheduling_ctx(tmp_path / "sched")
subagent_id = configure_test_subagent(
monkeypatch, subagent_id="session-actor", kind="agent_session",
target="claude=route-a",
)
scheduled = _schedule_task(
sched_ctx, subagent_id=subagent_id, objective="o", expected_output="e",
)
assert "CAPABILITY_DELTA" not in scheduled
assert "subagent_id=session-actor" in scheduled
assert "route=agent_session" in scheduled
def test_a_child_that_got_what_was_asked_stays_quiet(tmp_path, monkeypatch):
"""A warning that always fires is not a warning. Nothing was taken away here, so
no block reaches the child and no delta reaches the parent's readback — `auto`
resolving to a concrete executor is the absence of a preference, not a loss."""
from ouroboros.agent import capability_delta_prompt_block
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", "provider::cheap")
task, dispatch = _dispatched(tmp_path / "quiet", monkeypatch, parent_lane="light")
assert task["capability_delta"]["reduced"] is False
assert task["capability_delta"]["effective_executor"] == "native"
assert capability_delta_prompt_block(dispatch) == ""
def test_an_explicit_harness_pin_is_a_typed_blocker_not_a_paid_reroute(tmp_path, monkeypatch):
"""D28, the owner's words: at an EXPLICIT `executor: harness` an unavailable route
stays a TYPED BLOCKER — «деньги API не тратятся без явного выбора». The §8 ban #12
exception (fall back to another route) is AUTO-ONLY.
This resolved to `native` with a loud `capability_delta`, which discloses the wrong
thing: however loudly it is announced, re-routing the pin to native execution
spends exactly the metered money the parent refused. The reason string matches
`cxi/p34-converged`'s rule table so synthesis adopts that table without a
behavioural diff (synthesis hazard H1)."""
from ouroboros import subagents as sub
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
# EXPLICIT harness, no route: blocked, and the block is one predicate.
task, dispatch = _dispatched(tmp_path / "pinned", monkeypatch, executor="harness")
assert dispatch.executor == "blocked" and dispatch.blocked is True
assert dispatch.route == ""
assert task["effective_executor"] == "blocked"
assert dispatch.delta.reason == "configured_session_route_unavailable"
assert dispatch.delta.reduced is True
# AUTO with no route: native, and quiet — nothing was asked for.
auto_task, auto_dispatch = _dispatched(tmp_path / "auto", monkeypatch, executor="auto")
assert auto_dispatch.executor == "native" and auto_dispatch.blocked is False
assert auto_task["capability_delta"]["reduced"] is False
# The whole rule table, at the SURVIVING resolver (p34's typed table — H1):
# `route` is a DelegationRoute or None, and the outcome is a typed record.
route_a = sub.DelegationRoute(route_id="route-a")
def row(requested, route):
res = sub.resolve_subagent_executor(requested, route=route)
return res.executor, res.reason
assert row("harness", None) == ("blocked", "harness_not_configured")
assert row("harness", route_a) == ("harness", "harness_ready")
assert row("auto", None) == ("native", "harness_not_configured")
assert row("auto", route_a) == ("harness", "harness_ready")
assert row("native", None) == ("native", "requested_native")
# An exhausted subscription window blocks a PIN and falls auto back, loudly.
spent = sub.resolve_subagent_executor("harness", route=route_a, reset_at="2030-01-01T00:00:00Z")
assert (spent.executor, spent.reason) == ("blocked", "subscription_window_exhausted")
assert sub.resolve_subagent_executor("auto", route=route_a, reset_at="X").executor == "native"
# `blocked` is a resolution OUTCOME, never a request a parent may make.
assert "blocked" not in sub.SUBAGENT_EXECUTORS
def test_blocked_configured_session_terminals_unrun_without_a_model_round(tmp_path, monkeypatch):
"""Charter D2 (owner 2026-08-28, N2=A): a blocked configured session ends the
task UNRUN and typed at $0 — never a model episode, never host API fallback.
The dc4c0204 wake-the-nanny behavior this test used to pin is retired."""
from ouroboros import agent as agent_module
from ouroboros.agent import Env, OuroborosAgent
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setattr(OuroborosAgent, "_log_worker_boot_once", lambda self: None)
monkeypatch.setattr("ouroboros.agent.build_llm_messages", lambda **kwargs: ([], {}))
calls: list = []
def _never(**kwargs):
calls.append(kwargs)
return "the model was called", {}, {"reasoning_notes": [], "tool_calls": []}
monkeypatch.setattr(agent_module, "run_llm_loop", _never)
repo = tmp_path / "repo"
repo.mkdir()
drive = tmp_path / "drive"
drive.mkdir()
pinned = _enqueue_through_supervisor(tmp_path / "sched", monkeypatch, executor="harness")
pinned.update({"id": "pinned1", "chat_id": 1, "drive_root": str(drive)})
agent = OuroborosAgent(Env(repo_dir=repo, drive_root=drive))
agent._handle_task_scoped(dict(pinned))
assert calls == [], "a blocked pin must terminal unrun with zero model rounds"
from ouroboros import delegate_custody as custody
custody_events = [
json.loads(line)
for line in custody.event_log_path(
pathlib.Path(pinned["budget_drive_root"])
).read_text(encoding="utf-8").splitlines()
if line.strip()
]
assert any(
evt.get("type") == "delegate_run_configured_startup_fault"
and evt.get("host_fallback") is False
for evt in custody_events
)
# The durable record keeps the selected session intent and typed failure.
result_path = drive / "task_results" / "pinned1.json"
record = json.loads(result_path.read_text(encoding="utf-8"))
assert record["effective_executor"] == "blocked"
assert record["model"] == "provider::parent"
assert record["configured_subagent"]["selected_subagent_id"] == "session-actor"
assert record["capability_delta"]["reason"] == "configured_session_route_unavailable"
assert float(record.get("cost_usd") or 0.0) == 0.0
assert "NOT run on metered API tokens" in str(record.get("result") or "")
def test_legacy_heavy_migration_selects_an_exact_api_model(tmp_path, monkeypatch):
"""The compatibility fixture becomes an exact actor, never a live lane fallback."""
from ouroboros.agent import capability_delta_prompt_block
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "")
task, dispatch = _dispatched(tmp_path / "noheavy", monkeypatch, model_lane="heavy")
assert task["effective_model_lane"] == "main"
assert task["model"] == "provider::main"
assert task["capability_delta"]["reason"] == ""
assert capability_delta_prompt_block(dispatch) == ""
# ...and a configured Heavy slot is honored silently.
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "provider::strong")
task, dispatch = _dispatched(tmp_path / "heavy", monkeypatch, model_lane="heavy")
assert task["effective_model_lane"] == "main"
assert task["model"] == "provider::strong"
assert capability_delta_prompt_block(dispatch) == ""
def test_legacy_model_global_ceiling_is_not_dispatch_authority(tmp_path, monkeypatch):
"""Scheduling reports requested effort; exact wire disclosure owns adaptation."""
from ouroboros.agent import capability_delta_prompt_block
from ouroboros.llm import LLMClient
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", "provider::cheap")
monkeypatch.setenv("OUROBOROS_EFFORT_TASK", "max")
monkeypatch.setitem(LLMClient._EFFORT_CEILING_CACHE, "provider::cheap", "low")
task, dispatch = _dispatched(
tmp_path / "ceiling", monkeypatch, model_lane="light", _actor_effort="max",
)
delta = task["capability_delta"]
assert (delta["derived_effort"], delta["effective_effort"]) == ("max", "max")
assert delta["reason"] == ""
assert "effort" not in capability_delta_prompt_block(dispatch)
assert task["reasoning_effort"] == "max"
# An effort inside the band is not a delta.
monkeypatch.setenv("OUROBOROS_EFFORT_TASK", "low")
_task, dispatch = _dispatched(
tmp_path / "inband", monkeypatch, model_lane="light", _actor_effort="low",
)
assert capability_delta_prompt_block(dispatch) == ""
def test_a_legacy_parent_lane_does_not_break_scheduling_or_dispatch(tmp_path, monkeypatch):
"""Inheritance reads the PARENT's stored lane, and durable data outlives the schema
that wrote it. A pre-v6.39 `code` on the parent's record must not turn every child
it spawns into an uncaught ValueError — the public schema stays strict about what
a CALLER may ask for, which is a different question."""
from ouroboros.tools.control import _schedule_task
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
task, _ = _dispatched(tmp_path / "legacy", monkeypatch, parent_lane="code")
assert task["effective_model_lane"] == "main"
# A caller asking for it directly is still refused.
ctx = _light_lane_ctx(tmp_path / "asked", monkeypatch)
assert "subagent_selection_required" in _schedule_task(
ctx, objective="o", expected_output="e", model_lane="code")
def test_the_completion_envelope_is_built_from_the_same_mapping_as_the_scheduler():
"""The envelope a RAN child publishes and the one the scheduler wrote are twins,
and they were two field-by-field mappings in two modules. They had already
drifted: the completion side re-derived the effective-lane fallback as a
hardcoded `light`, so a record missing that field came back describing a lane
the resolver would never produce — and the delta axes had to be added twice."""
from ouroboros.subagents import envelope_from_task
delta = {"requested_executor": "harness", "effective_executor": "native", "reduced": True}
env = envelope_from_task(
{"id": "c1", "model_lane": "", "requested_executor": "harness",
"effective_model_lane": "heavy", "effective_executor": "native",
"executor_route": "", "tool_profile": "acting_subagent",
"capability_delta": delta},
status="completed", usage={"rounds": 3},
)
assert env["effective_lane"] == "heavy"
assert env["executor"] == "harness"
assert env["effective_executor"] == "native"
assert env["tool_profile"] == "acting_subagent"
assert env["capability_delta"]["reduced"] is True
assert env["usage"]["rounds"] == 3
# A record with NO resolution on it describes no lane, rather than substituting
# one: "not dispatched" and "ran on the lane of record" are different facts.
assert envelope_from_task({"id": "c2"}, status="requested")["effective_lane"] == ""
def test_lane_rank_is_the_only_lane_ordering(tmp_path):
"""One comparison decides "weaker than what was asked" for every axis. Effort
already had `config.effort_rank`; the lane had nothing, so the question was
simply never asked. `auto` has no rank — it is a request to inherit, not a
strength, so the thing an effective lane is measured against is the lane the
request RESOLVED FROM, never the literal `auto`."""
from ouroboros.subagents import LANE_STRENGTH, lane_is_weaker, lane_rank
assert LANE_STRENGTH == ("light", "main", "heavy")
assert lane_rank("light") < lane_rank("main") < lane_rank("heavy")
assert lane_rank("auto") == -1
assert lane_rank("code") == -1
assert lane_is_weaker("main", "heavy") is True
assert lane_is_weaker("heavy", "main") is False
# Nothing can rank below `auto`, which is why comparing against it was a
# disclosure that could never fire.
assert lane_is_weaker("light", "auto") is False
def test_intended_lane_is_the_one_owner_of_what_a_request_means(tmp_path):
"""`auto` means "the parent's lane". Two places need that answer and neither may
own it: the resolution measures the effective lane against it, and the ADMISSION
gate for a `require_lane` constraint runs before the child is dispatched, so it
cannot ask what lane the child ended up on. One predicate, two readers."""
from ouroboros.subagents import LANE_OF_RECORD, intended_lane, resolve_subagent_lane
assert intended_lane("auto", "heavy") == "heavy"
assert intended_lane("light", "heavy") == "light"
assert intended_lane("auto", "") == LANE_OF_RECORD
# Stored garbage on either side must not make a child unschedulable.
assert intended_lane("auto", "code") == LANE_OF_RECORD
assert intended_lane("code", "heavy") == "heavy"
# The resolution asks this predicate rather than re-deriving it.
assert resolve_subagent_lane("auto", parent_lane="heavy").resolved_from == "heavy"
# ------------------------------------------------- v6.87.27: the twins that were missed
def test_configured_actor_does_not_inherit_a_stale_heavy_parent(tmp_path, monkeypatch):
"""A historical parent lane cannot silently rewrite an explicit actor snapshot."""
from ouroboros.agent import capability_delta_prompt_block
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "")
task, dispatch = _dispatched(tmp_path / "inherited-noheavy", monkeypatch, parent_lane="heavy")
delta = task["capability_delta"]
assert (delta["requested_lane"], delta["resolved_lane"], delta["effective_lane"]) == (
"auto", "main", "main")
assert delta["reduced"] is False
assert delta["reason"] == ""
assert task["effective_model_lane"] == "main"
assert task["model"] == "provider::main"
assert capability_delta_prompt_block(dispatch) == ""
# An inherited lane the install CAN provide still takes nothing away.
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "provider::strong")
_ok, quiet = _dispatched(
tmp_path / "inherited-ok", monkeypatch, parent_lane="heavy", model_lane="heavy",
)
assert capability_delta_prompt_block(quiet) == ""
def test_scheduler_effort_stays_pre_wire_until_exact_route_disclosure(tmp_path, monkeypatch):
"""Legacy floors stay diagnostic; physical request usage reports any adjustment."""
from ouroboros.agent import capability_delta_prompt_block
from ouroboros.llm import LLMClient
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", "provider::cheap")
monkeypatch.setenv("OUROBOROS_EFFORT_TASK", "none")
monkeypatch.setitem(LLMClient._EFFORT_FLOOR_CACHE, "provider::cheap", "low")
monkeypatch.setitem(LLMClient._EFFORT_FLOOR_LOADED, "provider::cheap", float("inf"))
task, dispatch = _dispatched(
tmp_path / "floor", monkeypatch, model_lane="light", _actor_effort="none",
)
delta = task["capability_delta"]
assert LLMClient.clamp_effort_for_route("provider::cheap", "none") == "low"
assert delta["effective_effort"] == "none"
assert delta["reduced"] is False
assert capability_delta_prompt_block(dispatch) == ""
# ...and it must not become a false alarm when ANOTHER axis opens the block: a
# raised effort inside a real reduction still is not something taken away.
_both, both_dispatch = _dispatched(
tmp_path / "floor-and-pin", monkeypatch, model_lane="light", executor="harness",
_actor_effort="none",
)
block = capability_delta_prompt_block(both_dispatch)
assert "executor harness->blocked" in block
assert "effort none->low" not in block
def test_a_require_lane_refusal_states_the_facts_not_the_lane_default(tmp_path):
"""The refusal is read by the model at the exact moment it is deciding how to fix
a rejected spawn, and it restated a default owned three modules away in
`subagents`. That copy went stale in v6.87.7, was corrected in v6.87.14 and went
stale AGAIN in v6.87.26 — it told the model an omitted lane resolves to `light`
while the code inherits the parent's. It now states only what the reducer holds,
and it is measured against the INTENDED lane, because admission runs before the
child is dispatched and the effective lane does not exist yet."""
from ouroboros.tools.control_delegation import effective_delegation_budget
row = {"payload": {"constraint_id": "c1", "directive": "require_lane",
"scope": {"lane": "heavy"}}}
refusal = effective_delegation_budget(
{}, unresolved_constraints=[row], role="critic",
requested_lane="auto", intended_lane="main")
assert refusal.ok is False
assert refusal.reason_code == "delegation_constraint_require_lane"
# No claim about what an omitted lane means — that rule is not this module's.
assert "v6.87" not in refusal.detail
# The facts it does hold, and a REACHABLE remedy: "ask for the lane explicitly"
# is not one when the install has no such slot, so the constraint has to give.
assert "'heavy'" in refusal.detail and "'auto'" in refusal.detail and "'main'" in refusal.detail
assert "override_delegation_constraint('c1')" in refusal.detail
# An omitted lane that INHERITS the required one is admitted: the gate reads the
# same predicate the resolution does, so it cannot disagree with it about `auto`.
from ouroboros.subagents import intended_lane
ok = effective_delegation_budget(
{}, unresolved_constraints=[row], role="critic", requested_lane="auto",
intended_lane=intended_lane("auto", "heavy"))
assert ok.ok is True
def test_the_admission_gate_asks_the_predicate_rather_than_the_raw_request(tmp_path, monkeypatch):
"""The WIRING, not the reducer. The reducer above is pure and can be handed
anything; what decides whether a real spawn is admitted is what the SUPERVISOR
passes it. Handing it the raw request means `auto` is compared verbatim against a
required lane, so a Heavy parent whose omitted-lane child INHERITS Heavy — the
v6.87.26 default, and the common case — is rejected for asking for the very lane
the constraint demands."""
import ouroboros.task_tree_ledger as ledger
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "provider::strong")
monkeypatch.setattr(
ledger, "open_delegation_constraints",
lambda _root: [{"payload": {"constraint_id": "c1", "directive": "require_lane",
"scope": {"lane": "heavy"}}}])
task = _enqueue_through_supervisor(tmp_path, monkeypatch, parent_lane="heavy")
assert task["parent_model_lane"] == "heavy"
assert task["requested_model_lane"] == "auto"
def test_the_parent_sees_the_reduction_when_it_reads_the_childs_answer(tmp_path):
"""The TERMINAL parent-facing disclosure, and since v6.87.28 the only one: the
reduction is not known until the child is dispatched, so a scheduling result
cannot carry it. This is also the moment the parent cares most — it is reading
the ANSWER that decides whether to trust a weaker result."""
from ouroboros.task_results import write_task_result
from ouroboros.tools.control import _get_task_result
ctx = _scheduling_ctx(tmp_path / "readback")
reduced = {"requested_lane": "heavy", "resolved_lane": "heavy", "effective_lane": "main",
"requested_executor": "harness", "effective_executor": "native",
"reason": "lane_slot_unavailable=heavy", "reduced": True}
write_task_result(tmp_path / "readback", "child1", "completed",
result="done", capability_delta=reduced)
out = _get_task_result(ctx, "child1")
assert "capability_delta" in out
assert "lane_slot_unavailable=heavy" in out
# A delta that took nothing away and ignored nothing is noise in every payload.
write_task_result(tmp_path / "readback", "child2", "completed",
result="done", capability_delta={**reduced, "reduced": False})
assert "capability_delta" not in _get_task_result(ctx, "child2")
# ...but an IGNORED legacy field is something to say, even without a reduction.
write_task_result(tmp_path / "readback", "child3", "completed", result="done",
capability_delta={"reduced": False, "legacy_note": "reasoning_effort='max' ignored"})
assert "legacy_note" in _get_task_result(ctx, "child3")
def test_the_batch_absorb_discloses_the_reduction_too(tmp_path):
"""The TWIN of the single-child read, and the one a fan-out parent actually uses.
A parent absorbs children through two surfaces: `get_task_result`/`wait_task` read
one child in full, and `wait_tasks` projects a batch compactly — which is the
right tool for "five independent children scheduled in one burst" by its own tool
description. The delta reached the first and not the second, so the parent most
likely to have several weakened children was the one told about none of them. The
compact projection is a DISCLOSED omission of forensics; a capability reduction is
not forensics, it is what decides how far to trust the answer."""
import json as _json
from ouroboros.task_results import write_task_result
from ouroboros.tools.control import _get_task_result, _wait_for_tasks
ctx = _scheduling_ctx(tmp_path / "batch")
reduced = {"requested_lane": "heavy", "resolved_lane": "heavy", "effective_lane": "main",
"requested_executor": "harness", "effective_executor": "native",
"reason": "lane_slot_unavailable=heavy", "reduced": True}
root = tmp_path / "batch"
write_task_result(root, "c1", "completed", result="done", capability_delta=reduced)
write_task_result(root, "c2", "completed", result="done",
capability_delta={**reduced, "reduced": False, "legacy_note": ""})
batch = _json.loads(_wait_for_tasks(ctx, ["c1", "c2"], timeout_sec=1))["tasks"]
assert batch["c1"]["capability_delta"]["reason"] == "lane_slot_unavailable=heavy"
# The same predicate decides both surfaces, so they cannot disagree about which
# deltas are worth saying.
assert "capability_delta" not in batch["c2"]
assert ("capability_delta" in _get_task_result(ctx, "c1")) is True
assert ("capability_delta" in _get_task_result(ctx, "c2")) is False
def test_one_resolution_writes_every_derived_field(tmp_path, monkeypatch):
""""Not two resolvers, not two records." Every derived field on a child's record
comes from `SubagentDispatch.record_fields()`, so an added axis is one edit rather
than a field-by-field mapping repeated in four modules that drift apart a release
later — and no OTHER surface may mint one."""
from ouroboros.agent import resolve_dispatch_axes
from ouroboros.subagents import SUBAGENT_INTENT_FIELDS, resolve_subagent_dispatch
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
task = {"id": "c1", "type": "task", "delegation_role": "subagent",
"requested_model_lane": "auto", "parent_model_lane": "heavy",
"requested_executor": "auto"}
before = dict(task)
dispatch = resolve_dispatch_axes(task)
derived = dispatch.record_fields()
assert set(derived) == {
"effective_model_lane", "model", "use_local_model", "reasoning_effort",
"effective_executor", "executor_route", "tool_profile", "capability_delta"}
# Nothing derived leaks into the intent half, and nothing intended is rewritten.
assert not set(derived) & set(SUBAGENT_INTENT_FIELDS)
for key in SUBAGENT_INTENT_FIELDS:
assert task.get(key) == before.get(key)
# The resolution is a pure function of the record: asking twice answers twice.
assert resolve_subagent_dispatch(before, task_type="task").record_fields() == derived
# A task that is not a delegated child is not resolved at all.
root = {"id": "r1", "type": "task"}
assert resolve_dispatch_axes(root) is None
assert "capability_delta" not in root
def test_queue_snapshot_projects_every_scheduling_intent_field(monkeypatch, tmp_path):
"""R2-3 (F9 delta): a PENDING child's queue-snapshot row is all a restarted
supervisor has, so an intent field missing from the projection is silently
dropped across restart — `required_model_lane` was, re-opening the
auto+harness⇒light default over a gate-verified lane. Walk
SUBAGENT_INTENT_FIELDS against the REAL projection so no future intent
field can be dropped the same way."""
import json as _json
from ouroboros.subagents import SUBAGENT_INTENT_FIELDS
from supervisor import queue as queue_mod
pending: list = []
running: dict = {}
queue_mod.init_queue_refs(pending, running, {"value": 0})
monkeypatch.setattr(queue_mod, "QUEUE_SNAPSHOT_PATH",
tmp_path / "queue_snapshot.json")
task = {"id": "t-intent-pin", "type": "task"}
sentinels = {name: f"sentinel-{i}" for i, name in enumerate(SUBAGENT_INTENT_FIELDS)}
task.update(sentinels)
pending.append(task)
assert queue_mod.persist_queue_snapshot(reason="intent-field-pin") is True
snapshot = _json.loads((tmp_path / "queue_snapshot.json").read_text(encoding="utf-8"))
row = snapshot["pending"][0]["task"]
for name, value in sentinels.items():
assert row.get(name) == value, (
f"scheduling intent field {name!r} is missing from the pending "
"queue-snapshot projection (supervisor/queue.py) — a restart would "
"silently drop it")
def test_a_stored_auto_parent_lane_is_the_lane_of_record_not_the_cheapest(monkeypatch):
"""A task record can legitimately carry the literal `auto` as its effective lane —
the supervisor falls that field back to the REQUESTED lane, which is `auto`
whenever a task was queued without a resolved one. Its children read it as
`parent_lane`, and `auto` is not a strength: unhandled it reached `_lane_model`
as an unknown lane, whose fall-through was the LIGHT model. The child dropped to
the cheapest route on this install, silently, and called the lane `auto`."""
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", "provider::cheap")
res = subagents.resolve_subagent_lane("auto", parent_lane="auto")
assert res.effective_lane == subagents.LANE_OF_RECORD == "main"
assert res.model == "provider::main"
# The fall-through itself: an unknown lane is "no lane on record", not Light.
assert subagents._lane_model("code") == "provider::main"
def test_prompt_block_omits_the_broken_below_phrase_on_an_executor_only_delta():
"""reduced=True with NO disclosable axis is the auto-fallback case (the axis
renderer deliberately keeps a non-pinned executor out of the list): the block
used to render "You are running BELOW what your parent asked for: " over an
empty list — a broken sentence duplicating dispatch_executor_note's job."""
from types import SimpleNamespace
from ouroboros.agent import capability_delta_prompt_block
class _Delta:
def as_dict(self):
return {
"requested_lane": "auto", "resolved_lane": "main",
"effective_lane": "main", "derived_effort": "",
"effective_effort": "", "requested_executor": "auto",
"effective_executor": "native",
"reason": "subscription_window_exhausted",
"reduced": True, "legacy_note": "",
}
block = capability_delta_prompt_block(
SimpleNamespace(delta=_Delta(), executor_resolution=None))
assert "BELOW what your parent asked" not in block
assert block == "" # nothing else to say either: the executor note owns it
# ---------------------------------------------------------------- B2: light-lane nanny policy
def _harness_ready_dispatch(monkeypatch):
"""Force the executor axis to a healthy harness route without a live daemon."""
route = subagents.DelegationRoute(route_id="codex")
monkeypatch.setattr(
subagents, "dispatch_executor_resolution",
lambda task: subagents.resolve_subagent_executor("auto", route=route),
)
def test_auto_lane_on_harness_executor_defaults_to_light_by_policy(monkeypatch):
"""B2 (poltergeist phase B): a harness-dispatched child whose request said
`auto` is a NANNY — its own rounds are custody chores around a $0 delegated
run, so the dispatch policy resolves it to the LIGHT lane instead of the
parent's expensive lane, and the provenance says the POLICY answered."""
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", "provider::cheap")
_harness_ready_dispatch(monkeypatch)
dispatch = subagents.resolve_subagent_dispatch(
{"id": "c1", "type": "task", "requested_model_lane": "auto",
"parent_model_lane": "main"},
task_type="task",
)
assert dispatch.executor == "harness"
assert dispatch.lane.effective_lane == "light"
assert dispatch.lane.model == "provider::cheap"
assert dispatch.lane.provenance == "policy"
assert dispatch.delta.as_dict()["lane_provenance"] == "policy"
# Not a reduction relative to itself: the policy IS the resolved baseline.
assert dispatch.lane.reduced is False
def test_explicit_lane_always_wins_over_the_harness_policy(monkeypatch):
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "provider::strong")
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", "provider::cheap")
_harness_ready_dispatch(monkeypatch)
dispatch = subagents.resolve_subagent_dispatch(
{"id": "c2", "type": "task", "requested_model_lane": "heavy"},
task_type="task",
)
assert dispatch.executor == "harness"
assert dispatch.lane.effective_lane == "heavy"
assert dispatch.lane.model == "provider::strong"
assert dispatch.lane.provenance == "requested"
def test_a_required_lane_wins_over_the_harness_policy_default(monkeypatch):
"""F9 (sol #1) admission→dispatch consistency: a child ADMITTED under a
satisfied `require_lane` constraint (auto request, parent on the required
lane) carries `required_model_lane` on its record — and the dispatch policy
default (auto+harness ⇒ light) must NOT apply over it. With the policy
suppressed, `auto` inherits the parent's lane, which is exactly the lane the
gate verified; the provenance honestly says "inherited"."""
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "provider::strong")
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", "provider::cheap")
_harness_ready_dispatch(monkeypatch)
dispatch = subagents.resolve_subagent_dispatch(
{"id": "c-req", "type": "task", "requested_model_lane": "auto",
"parent_model_lane": "heavy", "required_model_lane": "heavy"},
task_type="task",
)
assert dispatch.executor == "harness"
assert dispatch.lane.effective_lane == "heavy"
assert dispatch.lane.model == "provider::strong"
assert dispatch.lane.provenance == "inherited"
# Stored garbage in the field is ignored — the policy applies as usual.
garbage = subagents.resolve_subagent_dispatch(
{"id": "c-junk", "type": "task", "requested_model_lane": "auto",
"parent_model_lane": "heavy", "required_model_lane": "warp-lane"},
task_type="task",
)
assert garbage.lane.effective_lane == "light"
assert garbage.lane.provenance == "policy"
def test_preflight_native_fallback_reresolves_without_the_harness_policy(monkeypatch):
"""F10 (sol #2, probe `native light policy`): a harness dispatch falsified at
the toolset preflight falls back to NATIVE — and must not stay on the
policy-light lane/cheap model the harness resolution chose. The fallback
re-resolves lane/model/effort as a native dispatch would (parent
inheritance), and the record, delta and envelope all describe it."""
from types import SimpleNamespace
from ouroboros.agent import preflight_delegate_visibility, resolve_dispatch_axes
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "provider::strong")
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", "provider::cheap")
_harness_ready_dispatch(monkeypatch)
task = {"id": "c-fb", "type": "task", "delegation_role": "subagent",
"requested_model_lane": "auto", "parent_model_lane": "heavy",
"requested_executor": "auto"}
dispatch = resolve_dispatch_axes(task)
assert dispatch.lane.effective_lane == "light" # the harness policy, pre-preflight
assert task["model"] == "provider::cheap"
tools = SimpleNamespace(available_tools=lambda: ["read_file", "web_search"])
amended, changed = preflight_delegate_visibility(tools, task, dispatch)
assert changed is True
assert amended.executor == "native"
# Lane and model re-resolved WITHOUT the harness policy: parent inheritance.
assert amended.lane.effective_lane == "heavy"
assert amended.lane.model == "provider::strong"
assert amended.lane.provenance == "inherited"
# Every stamped surface tells the re-resolved story.
assert task["effective_model_lane"] == "heavy"
assert task["model"] == "provider::strong"
assert task["effective_executor"] == "native"
assert task["capability_delta"]["effective_lane"] == "heavy"
assert task["capability_delta"]["lane_provenance"] == "inherited"
assert "delegate_tools_invisible" in task["capability_delta"]["reason"]
assert task["capability_delta"]["reduced"] is True
assert task["subagent_envelope"]["effective_lane"] == "heavy"
assert task["subagent_envelope"]["model"] == "provider::strong"
assert task["subagent_envelope"]["effective_executor"] == "native"
def test_native_child_keeps_plain_inheritance(monkeypatch):
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_HEAVY", "provider::strong")
monkeypatch.setenv("OUROBOROS_SUBAGENT_HARNESS", "")
dispatch = subagents.resolve_subagent_dispatch(
{"id": "c3", "type": "task", "requested_model_lane": "auto",
"parent_model_lane": "heavy"},
task_type="task",
)
assert dispatch.executor == "native"
assert dispatch.lane.effective_lane == "heavy"
assert dispatch.lane.provenance == "inherited"
def test_policy_light_with_an_empty_light_slot_lands_main_and_says_so(monkeypatch):
"""The provenance names the DECISION source even when the slot outcome moves
the effective lane: policy said light, no light slot exists, the model is
Main — and the record must carry both facts, not blend them."""
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.delenv("OUROBOROS_MODEL_LIGHT", raising=False)
_harness_ready_dispatch(monkeypatch)
dispatch = subagents.resolve_subagent_dispatch(
{"id": "c4", "type": "task", "requested_model_lane": "auto"},
task_type="task",
)
assert dispatch.lane.provenance == "policy"
assert dispatch.lane.resolved_from == "light"
assert dispatch.lane.effective_lane == "main"
assert dispatch.lane.model == "provider::main"
def test_switch_model_never_rewrites_the_dispatch_lane_record(monkeypatch, tmp_path):
"""B2 acceptance-model provenance: the nanny raising itself for an acceptance
round is a ToolContext override (visible per-round in llm_usage rows), never a
rewrite of the durable dispatch resolution — the record keeps saying which
lane the child was DISPATCHED on."""
from ouroboros.tools.control import _switch_model
from ouroboros.tools.registry import ToolContext
monkeypatch.setenv("OUROBOROS_MODEL", "provider::main")
monkeypatch.setenv("OUROBOROS_MODEL_LIGHT", "provider::cheap")
ctx = ToolContext(repo_dir=tmp_path, drive_root=tmp_path)
record = {"effective_model_lane": "light", "model": "provider::cheap",
"capability_delta": {"lane_provenance": "policy"}}
ctx.task_metadata = dict(record)
out = _switch_model(ctx, model="provider::main")
assert "OK: switching" in out
assert ctx.active_model_override == "provider::main"
# The durable dispatch record is untouched — acceptance-round provenance is
# read from llm_usage (each round carries the REAL model), not from here.
assert {k: ctx.task_metadata[k] for k in record} == record
def test_switch_model_refuses_an_unknown_effort_instead_of_coercing(monkeypatch, tmp_path):
"""`effort` is validated like `model`: an unknown tier is refused rather than
silently coerced to `medium`, and the refusal takes the whole call with it —
a model switch requested in the same call is NOT applied."""
from ouroboros.config import EFFORT_SCALE
from ouroboros.tools.control import _switch_model
from ouroboros.tools.registry import ToolContext
monkeypatch.setattr(
"ouroboros.llm.LLMClient.available_models",
lambda self: ["provider::main"],
)
ctx = ToolContext(repo_dir=tmp_path, drive_root=tmp_path)
out = _switch_model(ctx, model="provider::main", effort="enormous")
assert out.startswith("⚠️ Unknown effort: enormous")
assert ", ".join(EFFORT_SCALE) in out
assert ctx.active_effort_override is None
assert ctx.active_model_override is None
# The new top tier is accepted, case/whitespace normalized like before.
assert "OK: switching" in _switch_model(ctx, effort=" ULTRA ")
assert ctx.active_effort_override == "ultra"
# Whitespace-only effort is neither refused nor applied: it stays an empty
# request and falls through to the listing, leaving no override behind.
blank = ToolContext(repo_dir=tmp_path, drive_root=tmp_path)
assert _switch_model(blank, effort=" ").startswith("Current available models:")
assert blank.active_effort_override is None
assert blank.active_model_override is None