22
33from datetime import datetime , timezone
44from hashlib import sha256
5+ import json
56from pathlib import Path
67import shutil
78
@@ -32,8 +33,7 @@ def _copy_example(tmp_path: Path) -> Path:
3233 return target
3334
3435
35- async def _build_fake_report (tmp_path : Path , run_id : str ):
36- root = _copy_example (tmp_path )
36+ async def _build_fake_report_for_root (root : Path , run_id : str ):
3737 prepared = prepare_run (root / "pipeline.json" , run_id = run_id )
3838 result = await run_fake_stage (prepared , scenario = "improve" )
3939 shutil .rmtree (Path (prepared .workspace .run_dir ) / "report" , ignore_errors = True )
@@ -60,6 +60,10 @@ async def _build_fake_report(tmp_path: Path, run_id: str):
6060 return prepared , report
6161
6262
63+ async def _build_fake_report (tmp_path : Path , run_id : str ):
64+ return await _build_fake_report_for_root (_copy_example (tmp_path ), run_id )
65+
66+
6367@pytest .mark .asyncio
6468async def test_publish_report_bundle_materializes_and_indexes_required_files (tmp_path ):
6569 prepared , report = await _build_fake_report (tmp_path , "artifact_complete" )
@@ -71,15 +75,62 @@ async def test_publish_report_bundle_materializes_and_indexes_required_files(tmp
7175 assert (report_dir / "optimization_report.json" ).is_file ()
7276 assert (report_dir / "optimization_report.md" ).is_file ()
7377 assert (report_dir / "artifact_index.json" ).is_file ()
74- for name in (
75- "baseline_train" ,
76- "baseline_validation" ,
77- "candidate_train" ,
78- "candidate_validation" ,
79- ):
80- assert (report_dir / "evaluations" / f"{ name } .json" ).is_file ()
8178 assert all (ref .relative_path != "report/artifact_index.json" for ref in index .artifacts )
8279
80+ input_paths = {
81+ ref .artifact_id : ref .relative_path
82+ for ref in index .artifacts
83+ if ref .artifact_type == "input"
84+ }
85+ assert input_paths == {
86+ "input.pipeline_config" : "report/inputs/pipeline_config.json" ,
87+ "input.optimizer_config" : "report/inputs/optimizer_config.json" ,
88+ "input.train_evalset" : "report/inputs/train_evalset.json" ,
89+ "input.validation_evalset" : "report/inputs/validation_evalset.json" ,
90+ }
91+ assert {
92+ ref .artifact_id : ref .relative_path
93+ for ref in index .artifacts
94+ if ref .artifact_type == "prompt"
95+ } == {
96+ "prompt.baseline.system_prompt" : (
97+ "report/prompts/baseline/000-system_prompt.md"
98+ ),
99+ "prompt.candidate.system_prompt" : (
100+ "report/prompts/candidate/000-system_prompt.md"
101+ ),
102+ }
103+ baseline_prompt = report_dir / "prompts" / "baseline" / "000-system_prompt.md"
104+ candidate_prompt = report_dir / "prompts" / "candidate" / "000-system_prompt.md"
105+ assert baseline_prompt .read_text (encoding = "utf-8" ) == (
106+ report .input_snapshot .prompt_snapshots [0 ].content
107+ )
108+ assert candidate_prompt .read_text (encoding = "utf-8" ) == (
109+ report .candidate .prompts ["system_prompt" ]
110+ )
111+ evaluation_paths = {
112+ ref .artifact_id : ref .relative_path
113+ for ref in index .artifacts
114+ if ref .artifact_type == "evaluation"
115+ }
116+ assert evaluation_paths == {
117+ f"evaluation.{ name } " : f"report/evaluations/{ name } .json"
118+ for name in (
119+ "baseline_train" ,
120+ "baseline_validation" ,
121+ "candidate_train" ,
122+ "candidate_validation" ,
123+ )
124+ }
125+ assert {
126+ path .name for path in (report_dir / "evaluations" ).iterdir ()
127+ } == {
128+ "baseline_train.json" ,
129+ "baseline_validation.json" ,
130+ "candidate_train.json" ,
131+ "candidate_validation.json" ,
132+ }
133+
83134 available = [ref for ref in index .artifacts if ref .status == "available" ]
84135 assert available
85136 for ref in available :
@@ -110,6 +161,36 @@ async def test_publish_rejects_input_hash_drift(tmp_path):
110161 assert list (run_dir .glob (".report.tmp-*" )) == []
111162
112163
164+ @pytest .mark .asyncio
165+ async def test_publish_rejects_plaintext_secret_in_optimizer_config_without_leaking_it (
166+ tmp_path ,
167+ ):
168+ root = _copy_example (tmp_path )
169+ optimizer_path = root / "optimizer.json"
170+ payload = json .loads (optimizer_path .read_text (encoding = "utf-8" ))
171+ secret = "sk-real-sentinel-secret-must-not-be-copied"
172+ payload ["optimize" ]["algorithm" ]["reflection_lm" ]["api_key" ] = secret
173+ optimizer_path .write_text (
174+ json .dumps (payload , ensure_ascii = False , indent = 2 ) + "\n " ,
175+ encoding = "utf-8" ,
176+ )
177+ prepared , report = await _build_fake_report_for_root (root , "artifact_secret" )
178+ run_dir = Path (prepared .workspace .run_dir )
179+
180+ with pytest .raises (ArtifactWriteError , match = "sensitive optimizer config" ):
181+ publish_report_bundle (report , run_dir = run_dir , copy_input_files = True )
182+
183+ assert secret in optimizer_path .read_text (encoding = "utf-8" )
184+ assert not (run_dir / "report" ).exists ()
185+ assert list (run_dir .glob (".report.tmp-*" )) == []
186+ secret_bytes = secret .encode ("utf-8" )
187+ assert all (
188+ secret_bytes not in path .read_bytes ()
189+ for path in run_dir .rglob ("*" )
190+ if path .is_file ()
191+ )
192+
193+
113194@pytest .mark .asyncio
114195async def test_publish_rejects_existing_report_directory (tmp_path ):
115196 prepared , report = await _build_fake_report (tmp_path , "artifact_existing" )
@@ -124,6 +205,34 @@ async def test_publish_rejects_existing_report_directory(tmp_path):
124205 )
125206
126207
208+ @pytest .mark .asyncio
209+ async def test_publish_does_not_replace_report_directory_created_during_staging (
210+ tmp_path , monkeypatch
211+ ):
212+ prepared , report = await _build_fake_report (tmp_path , "artifact_report_race" )
213+ run_dir = Path (prepared .workspace .run_dir )
214+ report_dir = run_dir / "report"
215+ original_write = artifact_writer ._write_text
216+
217+ def create_competing_report_before_publish (path , content ):
218+ original_write (path , content )
219+ if path .name == "artifact_index.json" :
220+ report_dir .mkdir ()
221+
222+ monkeypatch .setattr (
223+ artifact_writer ,
224+ "_write_text" ,
225+ create_competing_report_before_publish ,
226+ )
227+
228+ with pytest .raises (ArtifactWriteError , match = "already exists" ):
229+ publish_report_bundle (report , run_dir = run_dir , copy_input_files = True )
230+
231+ assert report_dir .is_dir ()
232+ assert list (report_dir .iterdir ()) == []
233+ assert list (run_dir .glob (".report.tmp-*" )) == []
234+
235+
127236@pytest .mark .parametrize ("target_inside_run" , [False , True ])
128237def test_discovery_rejects_any_file_symlink (tmp_path , target_inside_run ):
129238 run_dir = tmp_path / "run"
0 commit comments