test(toolbox): approach-id is the other half of the same composition -- and containment is reachable

`--run-id` was guarded as a class; `--approach-id` was not. `write_outbox` composes
`{run_id}-{approach_id}`, so the same escape ran through the unguarded half: measured 20.09,
`--approach-id a/../../../ESCAPE` answered 0 and put the artefacts three levels above the
directory the caller named.

Counted rather than assumed: 11 path compositions in `outbox.py`, two caller-supplied values in
them. Four forms x the one door approach-id reaches, each with the intermediate directory
created first -- without it two of the four were green at HEAD on a missing directory, which is
a reason that is not the guard.

The containment check is asserted where it IS reachable: the writers themselves, called
directly. That is the run path's own way in -- `run.py` hands its `--run-id` straight to
`outbox.write_*`, past the string rule that lives in the door -- so the arms are not vacuous.

10 arms red on ASSERT about behaviour: 4 approach-id (exit 0), 6 writer-level (DID NOT RAISE).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-20 12:52:26 +02:00
commit 8a6a09df05
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q

View file

@ -1,4 +1,4 @@
"""`--run-id` navngir en kjøring, ikke en sti — og de sju utboks-dørene må håndheve det.
"""Ingen kaller-oppgitt NAVN bærer en sti inn i et filnavn — og de sju utboks-dørene håndhever det.
Hver skriver bygger filnavnet sitt som ``<utkatalog>/<run_id>-<artefakt>.json``. Utkatalogen er
kallerens å navngi (det er en egen regel), men run-id-en gikk urørt inn i den sammensetningen:
@ -13,6 +13,18 @@ lagt igjen fila ville passert på den første alene.
rc-0-kontrollen er obligatorisk: samme argv med en ren run-id svare 0 og skrive. Uten den
måler armene ingenting en dør som avviste ALT ville vært like grønn som den riktige.
`--run-id` var bare den ene halvdelen av SAMME sammensetning. `write_outbox` bygger
``{run_id}-{approach_id}`` når en tilnærming er navngitt, og `--approach-id` gikk urørt inn:
``--approach-id a/../../../ESCAPE`` (etter at ``<ut>/<run_id>-a`` finnes) svarte 0 og la
artefaktene TRE nivåer over katalogen kalleren navnga. Telt som klasse: 11 stisammensetninger i
`outbox.py`, to kaller-oppgitte verdier i dem run-id og approach-id. Begge voktes her.
Og inneslutningen står igjen som SISTE ledd, men der den er OPPNÅELIG: i `outbox.py`, der stien
faktisk settes sammen. Strengregelen bor i døren, kjørestiens egne kall `outbox.write_*`
(og `run.py`s eget `--run-id`) går UTENOM den inneslutnings-armene under kaller skriverne
direkte og er derfor ikke vakuøse. Den verner også mot det NESTE flagget noen legger inn i et
filnavn: sammensetningen er ett sted, og sjekken sitter i den.
"""
from __future__ import annotations
@ -26,6 +38,8 @@ from typing import Any
import pytest
from portfolio_optimiser import outbox
_REPO = Path(__file__).resolve().parents[1]
_IR = _REPO / "shared" / "examples" / "bygg-energi-mikro" / "validator-input.json"
@ -216,3 +230,124 @@ def test_write_coverage_refuses_to_invent_a_stop_reason(tmp_path: Path) -> None:
assert med.returncode == 0, med.stdout + med.stderr
paa_disk = json.loads(Path(json.loads(med.stdout)["path"]).read_text(encoding="utf-8"))
assert paa_disk["stop_reason"] == ""
#: De fire formene, som approach-id-en de settes inn som. `write_outbox` bygger stammen
#: ``{run_id}-{approach_id}``, så approach-id-en er et SUFFIKS i sammensetningen og ikke et eget
#: ledd: `../../ESCAPE` alene ville truffet et ikke-eksisterende `<run_id>-..` og gitt en
#: FileNotFoundError — altså grønt av feil grunn. Derfor går den relative formen gjennom et ledd
#: som FINNES (`a/../../../`), som er nøyaktig formen som ble målt 20.09.
_TILNAERMING_FORMER: dict[str, str] = {
"relativ-opp-gjennom-eksisterende": "a/../../../ESCAPE",
"med-skilletegn": "under/ESCAPE",
"bare-punktum": "..",
"absolutt": "/ABSOLUTT",
}
@pytest.mark.parametrize("form", sorted(_TILNAERMING_FORMER))
def test_an_approach_id_that_carries_a_path_is_refused_and_writes_nothing(
tmp_path: Path, form: str
) -> None:
"""`--approach-id` × den ene døren den når. Samme sammensetning, samme krav.
Mellomkatalogen den sammensatte stien peker gjennom opprettes FØRST, og det er ikke pynt:
uten den feiler skrivingen en manglende katalog, og armen er grønn ved HEAD av en grunn
som ikke er vakten. Målt 20.09 to av de fire formene var nettopp dét. Med katalogen
plass svarer ALLE fire 0 og skriver ved HEAD, hver av dem skiller de to
implementasjonene."""
fikstur = tmp_path / "fikstur"
ut = tmp_path / "inni" / "dypere"
verdi = _TILNAERMING_FORMER[form]
(ut / f"{_REN}-{verdi}-proposal.json").parent.mkdir(parents=True, exist_ok=True)
_, oevrige = _DOERER["write-outbox"]
argv = oevrige(fikstur)
foer = _tree(tmp_path)
proc = _toolbox(
"write-outbox",
"--outbox-dir",
str(ut),
"--run-id",
_REN,
"--approach-id",
verdi,
*argv,
)
assert proc.returncode != 0, f"write-outbox godtok en stibærende approach-id: {proc.stdout}"
assert _tree(tmp_path) == foer, f"write-outbox la igjen en fil for {form}"
def test_the_same_call_with_a_clean_approach_id_writes(tmp_path: Path) -> None:
"""rc-0-kontrollen for approach-id-en. Samme argv, eneste forskjell er verdien — og
artefaktene bærer den utvidede stammen inne i katalogen kalleren navnga."""
fikstur = tmp_path / "fikstur"
ut = tmp_path / "inni" / "dypere"
_, oevrige = _DOERER["write-outbox"]
proc = _toolbox(
"write-outbox",
"--outbox-dir",
str(ut),
"--run-id",
_REN,
"--approach-id",
"a",
*oevrige(fikstur),
)
assert proc.returncode == 0, proc.stdout + proc.stderr
skrevet = json.loads(proc.stdout)
sti = Path(skrevet["proposal_path"])
assert sti.is_file()
assert sti.name == f"{_REN}-a-proposal.json"
assert sti.resolve().parent == ut.resolve(), "artefaktet havnet utenfor utkatalogen"
#: Skriverne bak de sju dørene, hver kalt DIREKTE — utenom strengregelen, som bor i døren.
#: Det er kjørestiens egen vei (`run.py` sender sin `--run-id` rett inn her), så inneslutningen
#: er oppnåelig her og armene er ikke vakuøse.
_SKRIVERE: dict[str, Callable[[str, str], Path]] = {
"write_run_config": lambda d, r: outbox.write_run_config(
d,
r,
profile="local",
resolved_models={"proposer": "gpt-x"},
max_rounds=5,
max_tokens=600000,
top_k=3,
),
"write_coverage": lambda d, r: outbox.write_coverage(d, r, rows=[], stop_reason="tokens"),
"write_prepass": lambda d, r: outbox.write_prepass(d, r, declaration={"bundle_id": "x"}),
"write_parse_failures": lambda d, r: outbox.write_parse_failures(d, r, failures=[]),
"write_proposal_reviews": lambda d, r: outbox.write_proposal_reviews(
d, r, payload={"reviews": []}
),
"write_debate_tools": lambda d, r: outbox.write_debate_tools(d, r, tool_calls=[]),
}
@pytest.mark.parametrize("writer", sorted(_SKRIVERE))
def test_the_writer_itself_refuses_a_path_that_leaves_the_directory(
tmp_path: Path, writer: str
) -> None:
"""Inneslutningen som SISTE ledd, der sammensetningen skjer. En ferdig sti som ikke resolverer
UNDER utkatalogen er en feil, ikke en fil og ingenting skal være skrevet når den felles."""
ut = tmp_path / "inni" / "dypere"
foer = _tree(tmp_path)
with pytest.raises(ValueError, match="utkatalogen|outside|outenfor|leaves"):
_SKRIVERE[writer](str(ut), "../../ESCAPE")
assert _tree(tmp_path) == foer, f"{writer} la igjen en fil for en rømmende sti"
@pytest.mark.parametrize("writer", sorted(_SKRIVERE))
def test_the_writer_itself_still_writes_a_contained_path(tmp_path: Path, writer: str) -> None:
"""rc-0-kontrollen for inneslutningen: samme kall med et rent navn skriver, inne i katalogen.
Uten den ville en skriver som nektet ALT vært like grønn som den riktige."""
ut = tmp_path / "inni" / "dypere"
sti = _SKRIVERE[writer](str(ut), _REN)
assert sti.is_file()
assert sti.resolve().parent == ut.resolve()