fix(run): S10 del 2 — post-mortem: stopp-artefakt, SDK-isolasjon, raw-JSON-direktiv
Transkript-analyse av den stoppede live-kjøringen (10 kall, 162 250 tokens, $0.331506): konfig-lekkasjen (setting_sources=None laster ALLE filsystem- settings) injiserte operatørens Claude-konfig i hvert kall — ~10-15k uncachede tokens, en påtvunget bekreftelses-preamble som gjorde ren-JSON-svar umulige, og en checker kapret av lekkede instrukser (debatt konvergerte aldri). - persist_stop_artifacts: stopp-event verbatim + usage/kost persisteres ALLTID ved BudgetExceeded (delt usage-shape med fullført-run-stien) - build_call_options: setting_sources=[] (SDK isolation mode, verifisert mot installert 0.2.110-kilde), system_prompt=None → tom system-prompt; detach- bevis via monkeypatchet query - _generation_prompt: krever ONLY the raw JSON object (fence-innpakning ga 4 fullpris parse-retries) 187/187 uten nøkkel · ruff + mypy --strict rene · tre detach-bevis RØDE → grønn Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QdSfQdND84oeq2mbjueLTS
This commit is contained in:
parent
0238507df4
commit
7637c6feae
7 changed files with 253 additions and 25 deletions
|
|
@ -15,7 +15,12 @@ from pathlib import Path
|
|||
|
||||
import pytest
|
||||
|
||||
from portfolio_optimiser_claude.artifacts import build_citations, persist_run_artifacts
|
||||
from portfolio_optimiser_claude.artifacts import (
|
||||
build_citations,
|
||||
persist_run_artifacts,
|
||||
persist_stop_artifacts,
|
||||
)
|
||||
from portfolio_optimiser_claude.budget import BudgetExceeded
|
||||
from portfolio_optimiser_claude.contracts import (
|
||||
ModelMapContract,
|
||||
TerminationContract,
|
||||
|
|
@ -216,3 +221,57 @@ def test_persistence_is_deterministic(tmp_path: Path) -> None:
|
|||
first = {name: path.read_bytes() for name, path in _persist(tmp_path / "a").items()}
|
||||
second = {name: path.read_bytes() for name, path in _persist(tmp_path / "b").items()}
|
||||
assert first == second
|
||||
|
||||
|
||||
# --- stop artifacts: usage + cost persisted even when the budget stops the run ---------------
|
||||
# The S10 live run stopped structurally on the token cap (§8) and persisted
|
||||
# NOTHING — real spend without a record. A budget stop is a run outcome.
|
||||
|
||||
|
||||
def _persist_stop(out_dir: Path) -> dict[str, Path]:
|
||||
return persist_stop_artifacts(
|
||||
out_dir,
|
||||
stop=BudgetExceeded("tokens", limit=150_000, observed=162_250),
|
||||
termination=TerminationContract(max_rounds=12, max_tokens=150_000),
|
||||
tokens_used=162_250,
|
||||
rounds_used=5,
|
||||
cost_usd=0.331506,
|
||||
)
|
||||
|
||||
|
||||
def test_budget_stop_persists_stop_event_and_usage(tmp_path: Path) -> None:
|
||||
paths = _persist_stop(tmp_path / "out")
|
||||
assert set(paths) == {"stop", "usage"}
|
||||
for path in paths.values():
|
||||
assert path.is_file()
|
||||
|
||||
|
||||
def test_stop_artifact_mirrors_the_stop_event_verbatim(tmp_path: Path) -> None:
|
||||
# LOAD-BEARING (§8/§11): the artifact carries the breached kind, limit and
|
||||
# observed value exactly as raised — never recomputed, never summarised.
|
||||
paths = _persist_stop(tmp_path / "out")
|
||||
assert json.loads(paths["stop"].read_text(encoding="utf-8")) == {
|
||||
"kind": "tokens",
|
||||
"limit": 150_000,
|
||||
"observed": 162_250,
|
||||
}
|
||||
|
||||
|
||||
def test_stop_usage_artifact_has_the_completed_run_shape(tmp_path: Path) -> None:
|
||||
# S11 reads ONE usage format: the stop path must write the same shape
|
||||
# (usage against caps + cost) as the completed-run path.
|
||||
paths = _persist_stop(tmp_path / "out")
|
||||
usage = json.loads(paths["usage"].read_text(encoding="utf-8"))
|
||||
assert usage == {
|
||||
"cost_usd": 0.331506,
|
||||
"max_rounds": 12,
|
||||
"max_tokens": 150_000,
|
||||
"rounds_used": 5,
|
||||
"tokens_used": 162_250,
|
||||
}
|
||||
|
||||
|
||||
def test_stop_persistence_is_deterministic(tmp_path: Path) -> None:
|
||||
first = {name: path.read_bytes() for name, path in _persist_stop(tmp_path / "a").items()}
|
||||
second = {name: path.read_bytes() for name, path in _persist_stop(tmp_path / "b").items()}
|
||||
assert first == second
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue