Oekt 17 found the class on four named files. This sweep ENUMERATES it: 42 negative substring assertions across 21 test files (STATE's "~34 across 23" was a premise -- measured, it is 42/21). Sixteen of them measured an absence without ever having shown presence; all sixteen now carry a positive control asserting the searched-for string PRESENT in the source artifact, in EXACTLY the form the negative looks for. Files touched: test_costsim, test_loop, test_okf (3 sites), test_preflight, test_run_entrance, test_s10_run_layer, test_sdk_version_guard, test_simulation (2 sites), test_step1_expel, test_step5_refine, test_step7_async_loop, test_step8_promotion, test_valuereport. VALUE-PROOF (green-without / red-with, per the oekt-17 rule that a detach proof is not a value proof). Seven source/fixture mutations, each making the negative vacuous: M1 verdict fixture loses the realization signal VALUE-PROVEN M2 decoy fixture loses its text VALUE-PROVEN M3 renderer stops emitting typed section headings VALUE-PROVEN M4 promotion stops writing the marker VALUE-PROVEN (pass 2) M5 fold stops rendering the realization surface VALUE-PROVEN M6 report stops labelling the cost section VALUE-PROVEN M7 preflight stops importing the SDK VALUE-PROVEN M4 needed pass 2: a PRECEDING assertion caught the same mutation, hiding the new control behind it -- the oekt-17 lesson reproduced. The remaining nine controls are vacuity guards (non-emptiness / form-presence) whose mutation would have to break the source artificially; they are stated as guards, not claimed as value-proven. MEASURED FINDING (test_loop): the FIRST-RUN-MARKER negative cannot be given a positive control at all. Within a run only the CHECKER's critique is fed back -- the proposer's own prior reasoning crosses no prompt boundary, not even within a run. So that negative holds trivially. Left in place with the limitation stated in the test rather than dressed up as a controlled seam; the CRITIQUE negative beside it IS controlled and is the real seam. Mutations were in-place on src/ and shared/ with original bytes restored and sha-verified; git status clean before and after. Suite 688 -> 688 (assertions added inside existing tests, no new test cases). ruff + mypy --strict green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Vc5PmZGjwuJypdhzKnJa5
224 lines
9.4 KiB
Python
224 lines
9.4 KiB
Python
"""Pre-run cost simulation (K6, S3.6-analog, D-I pkt. 3; paritetsrad 18).
|
||
|
||
The operator sees an ESTIMATED upper-bound USD cost for a (portfolio-)run BEFORE
|
||
any spend — a what-if over the models in model_map.json × effort levels. Every
|
||
seam here is load-bearing (§11): the RED-when-detached notes on each test name
|
||
the mutation that makes it fail, so a green-but-dead test can't hide.
|
||
|
||
Nothing here touches the network or a key (offline invariant): the estimate is
|
||
pure config arithmetic over schema-validated pricing config.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import ast
|
||
import json
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
from pydantic import ValidationError
|
||
|
||
from portfolio_optimiser_claude.contracts import (
|
||
ModelMapContract,
|
||
ModelPriceContract,
|
||
PricingContract,
|
||
load_pricing,
|
||
)
|
||
from portfolio_optimiser_claude.costsim import estimate_costs, main
|
||
|
||
SRC_PKG = Path(__file__).resolve().parents[1] / "src" / "portfolio_optimiser_claude"
|
||
|
||
|
||
def _model_map(*model_ids: str) -> ModelMapContract:
|
||
# Every profile needs a 'default' (§10); role split is irrelevant to the
|
||
# what-if, which iterates the DISTINCT model ids configured anywhere.
|
||
mapping = {"default": model_ids[0]}
|
||
for i, mid in enumerate(model_ids[1:], start=1):
|
||
mapping[f"role{i}"] = mid
|
||
return ModelMapContract(profiles={"anthropic": mapping})
|
||
|
||
|
||
def _pricing(**rates: float) -> PricingContract:
|
||
return PricingContract(
|
||
prices={
|
||
mid: ModelPriceContract(usd_per_mtok=rate, source="src", source_date="2026-01-01")
|
||
for mid, rate in rates.items()
|
||
}
|
||
)
|
||
|
||
|
||
class TestPricingSchemaFailsFast:
|
||
"""§10: a malformed/missing price is a startup schema error, never a guessed rate."""
|
||
|
||
def test_non_positive_price_is_rejected(self) -> None:
|
||
with pytest.raises(ValidationError):
|
||
ModelPriceContract(usd_per_mtok=0.0, source="src", source_date="2026-01-01")
|
||
|
||
def test_a_price_entry_without_a_rate_is_rejected(self) -> None:
|
||
with pytest.raises(ValidationError):
|
||
PricingContract(prices={"m": {"source": "src", "source_date": "2026-01-01"}})
|
||
|
||
def test_source_and_date_are_required(self) -> None:
|
||
# A price with no provenance would let a stale rate pass silently (§1).
|
||
with pytest.raises(ValidationError):
|
||
PricingContract(prices={"m": {"usd_per_mtok": 5.0}})
|
||
|
||
def test_empty_pricing_is_rejected(self) -> None:
|
||
with pytest.raises(ValidationError):
|
||
PricingContract(prices={})
|
||
|
||
|
||
class TestMissingPriceFailsFast:
|
||
"""RED (detach the price lookup guard → KeyError not ValueError): a model in
|
||
model_map with NO price stops the estimate cold — never a guessed rate."""
|
||
|
||
def test_a_configured_model_without_a_price_raises_named(self) -> None:
|
||
model_map = _model_map("m-cheap", "m-unpriced")
|
||
pricing = _pricing(**{"m-cheap": 1.0}) # m-unpriced deliberately absent
|
||
with pytest.raises(ValueError, match="missing price for m-unpriced"):
|
||
estimate_costs(model_map, pricing, n_projects=1, token_cap_per_project=1000)
|
||
|
||
|
||
class TestEstimateScalesAndIsDeterministic:
|
||
"""RED (detach the effort factor → efforts identical): the estimate scales
|
||
with BOTH model price and effort, and is byte-reproducible (no clock/random)."""
|
||
|
||
def test_cost_scales_linearly_with_model_price(self) -> None:
|
||
model_map = _model_map("m-cheap", "m-pricey")
|
||
pricing = _pricing(**{"m-cheap": 1.0, "m-pricey": 10.0})
|
||
est = estimate_costs(model_map, pricing, n_projects=3, token_cap_per_project=1000)
|
||
by_id = {m.model_id: m for m in est.models}
|
||
# Same portfolio, same effort → 10x price ⇒ 10x cost, per effort level.
|
||
for effort in ("low", "high", "max"):
|
||
cheap = next(e.cost_usd for e in by_id["m-cheap"].efforts if e.effort == effort)
|
||
pricey = next(e.cost_usd for e in by_id["m-pricey"].efforts if e.effort == effort)
|
||
assert pricey == pytest.approx(cheap * 10.0)
|
||
|
||
def test_cost_scales_strictly_with_effort(self) -> None:
|
||
# DETACH POINT: make every _EFFORT_FACTORS value equal (e.g. all 1.0) and
|
||
# this strict-increase assertion goes RED — the effort axis is dead.
|
||
model_map = _model_map("m")
|
||
pricing = _pricing(**{"m": 5.0})
|
||
est = estimate_costs(model_map, pricing, n_projects=2, token_cap_per_project=1000)
|
||
costs = [e.cost_usd for e in est.models[0].efforts]
|
||
assert costs == sorted(costs)
|
||
assert costs[0] < costs[-1]
|
||
|
||
def test_estimate_is_reproducible(self) -> None:
|
||
model_map = _model_map("m")
|
||
pricing = _pricing(**{"m": 7.0})
|
||
a = estimate_costs(model_map, pricing, n_projects=4, token_cap_per_project=1234)
|
||
b = estimate_costs(model_map, pricing, n_projects=4, token_cap_per_project=1234)
|
||
assert [(m.model_id, [(e.effort, e.cost_usd) for e in m.efforts]) for m in a.models] == [
|
||
(m.model_id, [(e.effort, e.cost_usd) for e in m.efforts]) for m in b.models
|
||
]
|
||
|
||
def test_non_positive_shape_is_rejected(self) -> None:
|
||
pricing = _pricing(**{"m": 5.0})
|
||
with pytest.raises(ValueError):
|
||
estimate_costs(_model_map("m"), pricing, n_projects=0, token_cap_per_project=1000)
|
||
with pytest.raises(ValueError):
|
||
estimate_costs(_model_map("m"), pricing, n_projects=1, token_cap_per_project=0)
|
||
|
||
|
||
class TestNoPriceLiteralInSource:
|
||
"""RED (hardcode any example rate in costsim.py): the grep-guard proves the
|
||
price MUST come from config — no rate literal lives in the module source."""
|
||
|
||
def test_no_example_price_appears_as_a_literal(self) -> None:
|
||
source = (SRC_PKG / "costsim.py").read_text(encoding="utf-8")
|
||
pricing = load_pricing() # the bundled data/pricing.example.json
|
||
# Positive controls: an empty price config would make the loop below iterate
|
||
# zero times, and an unreadable/empty source would make every substring miss —
|
||
# both green without guarding anything.
|
||
assert pricing.prices
|
||
assert "usd_per_mtok" in source
|
||
for model_id, price in pricing.prices.items():
|
||
assert str(price.usd_per_mtok) not in source, (
|
||
f"price for {model_id} is hardcoded in costsim.py — must come from config"
|
||
)
|
||
|
||
|
||
def _imported_module_names(module_path: Path) -> set[str]:
|
||
tree = ast.parse(module_path.read_text(encoding="utf-8"))
|
||
names: set[str] = set()
|
||
for node in ast.walk(tree):
|
||
if isinstance(node, ast.Import):
|
||
names.update(alias.name.split(".")[0] for alias in node.names)
|
||
elif isinstance(node, ast.ImportFrom) and node.level == 0 and node.module:
|
||
names.add(node.module.split(".")[0])
|
||
return names
|
||
|
||
|
||
class TestCostsimPurity:
|
||
"""LOAD-BEARING (§11): the cost sim imports no agent toolkit — offline by design."""
|
||
|
||
def test_costsim_never_imports_an_agent_toolkit(self) -> None:
|
||
names = _imported_module_names(SRC_PKG / "costsim.py")
|
||
assert not names & {"claude_agent_sdk", "anthropic"}
|
||
|
||
|
||
class TestBundledExampleAndCli:
|
||
"""Nøkkelantakelse: the bundled example prices COVER the model model_map
|
||
configures, so the offline default path runs green — and the CLI is offline."""
|
||
|
||
def test_bundled_pricing_validates(self) -> None:
|
||
pricing = load_pricing()
|
||
assert pricing.prices # non-empty, schema-valid
|
||
for price in pricing.prices.values():
|
||
assert price.usd_per_mtok > 0
|
||
assert price.source and price.source_date
|
||
|
||
def test_the_default_estimate_runs_on_bundled_config(self) -> None:
|
||
# The bundled model_map's model must have a bundled price — else the
|
||
# default CLI would fail-fast. This binds the two example files together.
|
||
from portfolio_optimiser_claude.contracts import _bundled_model_map
|
||
|
||
est = estimate_costs(
|
||
ModelMapContract(**_bundled_model_map()),
|
||
load_pricing(),
|
||
n_projects=1,
|
||
token_cap_per_project=150_000,
|
||
)
|
||
assert est.models
|
||
assert all(m.efforts for m in est.models)
|
||
|
||
def test_cli_prints_an_estimate_marked_estimat(
|
||
self, capsys: pytest.CaptureFixture[str]
|
||
) -> None:
|
||
rc = main([])
|
||
out = capsys.readouterr().out
|
||
assert rc == 0
|
||
assert "ESTIMAT" in out
|
||
|
||
def test_cli_honours_explicit_pricing_file(
|
||
self, tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
||
) -> None:
|
||
from portfolio_optimiser_claude.contracts import _bundled_model_map
|
||
|
||
model_id = ModelMapContract(**_bundled_model_map()).profiles["anthropic"]["default"]
|
||
pricing_file = tmp_path / "pricing.json"
|
||
pricing_file.write_text(
|
||
json.dumps(
|
||
{
|
||
"prices": {
|
||
model_id: {
|
||
"usd_per_mtok": 3.0,
|
||
"source": "src",
|
||
"source_date": "2026-01-01",
|
||
}
|
||
}
|
||
}
|
||
),
|
||
encoding="utf-8",
|
||
)
|
||
rc = main(["--projects", "2", "--token-cap", "1000", "--pricing", str(pricing_file)])
|
||
out = capsys.readouterr().out
|
||
assert rc == 0
|
||
assert model_id in out
|
||
|
||
|
||
def test_no_network_import_in_costsim() -> None:
|
||
# Belt on the offline invariant: not even the stdlib socket/urllib slip in.
|
||
names = _imported_module_names(SRC_PKG / "costsim.py")
|
||
assert not names & {"socket", "urllib", "http", "requests", "httpx"}
|