diff --git a/tests/test_rf100vl_train.py b/tests/test_rf100vl_train.py index 07c12ce..3d87aef 100644 --- a/tests/test_rf100vl_train.py +++ b/tests/test_rf100vl_train.py @@ -640,3 +640,42 @@ def test_keep_cache_opts_out_of_reclaiming_the_cache(tmp_path): assert cached.exists() assert freed == 2048 + + +def test_every_recipe_disables_periodic_snapshots(): + """LibreYOLO's TrainConfig defaults to save_period=10, so families whose + trainer honours it write a full checkpoint every 10 epochs: ~1000 files and + 150 GB across one campaign, on a 250 GB box. Nothing reads them. Resume + uses last.pt, selection uses best.pt, and the uploader ships best.pt. A + campaign already hit 97% disk on these alone.""" + for family in set(list_families()): + recipe = rf100vl_train.load_recipe( + rf100vl_train.recipe_path_for_family(family), + family=family, + ) + assert recipe["train"].get("save_period") == 0, ( + f"{family} does not disable save_period; periodic snapshots will " + "fill the campaign box" + ) + + +def test_submission_recipe_sha_reads_only_this_model(tmp_path): + """Submissions land in one shared directory that accumulates every earlier + campaign. Reading the newest of all of them compared a neighbour's recipe + against this run and refused the upload.""" + import json as _json + from va_bench import artifacts + + def write(name, sha): + (tmp_path / name).write_text( + _json.dumps({"rf100vl": {"recipe_sha256": sha}}), encoding="utf-8" + ) + + mine = "a" * 64 + theirs = "b" * 64 + write("ec-s__pytorch__cuda__x__20260101T000000Z.json", mine) + # sorts after ours, and is what the old code would have picked + write("yolox-m__pytorch__cuda__x__20260909T000000Z.json", theirs) + + assert artifacts._submission_recipe_sha(tmp_path, "ec-s") == mine + assert artifacts._submission_recipe_sha(tmp_path, "yolox-m") == theirs diff --git a/va_bench/artifacts.py b/va_bench/artifacts.py index 2d353c7..8af2553 100644 --- a/va_bench/artifacts.py +++ b/va_bench/artifacts.py @@ -381,11 +381,22 @@ class IncompletePublish(RuntimeError): """A publishable tier is missing artifacts that make the run reproducible.""" -def _submission_recipe_sha(submissions_dir: str | Path | None) -> str | None: - """recipe_sha256 recorded by the newest submission, or None.""" +def _submission_recipe_sha( + submissions_dir: str | Path | None, + model_key: str | None = None, +) -> str | None: + """recipe_sha256 recorded by this model's newest submission, or None. + + Submissions are written to one shared directory, so it accumulates every + earlier campaign's files. Reading the newest of ALL of them compared a + neighbouring model's recipe against this one and refused the upload: a + stale yolox submission blocked three ec-s attempts with a mismatch error + that named ec-s. Only this model's own submissions can describe its run. + """ if not submissions_dir: return None - paths = sorted(Path(submissions_dir).glob("*.json")) + pattern = f"{model_key}__*.json" if model_key else "*.json" + paths = sorted(Path(submissions_dir).glob(pattern)) for path in reversed(paths): try: with open(path, encoding="utf-8") as handle: @@ -528,7 +539,7 @@ def add(path: Path, repo_path: str, *, required: bool = False) -> None: # The resolver falls back to the packaged family recipe when --recipe is # omitted, which is the wrong file for any run that used a custom one. if publishable and recipe_path and Path(recipe_path).is_file(): - claimed = _submission_recipe_sha(submissions_dir) + claimed = _submission_recipe_sha(submissions_dir, model_key) if claimed: actual = _sha256(Path(recipe_path)) if actual != claimed: diff --git a/va_bench/recipes/rf100vl/deim.json b/va_bench/recipes/rf100vl/deim.json index 40bdd8e..35d0b7a 100644 --- a/va_bench/recipes/rf100vl/deim.json +++ b/va_bench/recipes/rf100vl/deim.json @@ -21,6 +21,7 @@ }, "sizes": {}, "train": { + "save_period": 0, "imgsz": 640, "optimizer": "adamw", "lr0": 0.0001, diff --git a/va_bench/recipes/rf100vl/deimv2.json b/va_bench/recipes/rf100vl/deimv2.json index 4b7ce3d..810dd44 100644 --- a/va_bench/recipes/rf100vl/deimv2.json +++ b/va_bench/recipes/rf100vl/deimv2.json @@ -25,6 +25,7 @@ "femto": {"imgsz": 416} }, "train": { + "save_period": 0, "imgsz": 640, "optimizer": "adamw", "lr0": 0.0005, diff --git a/va_bench/recipes/rf100vl/dfine.json b/va_bench/recipes/rf100vl/dfine.json index ff24b96..43c7431 100644 --- a/va_bench/recipes/rf100vl/dfine.json +++ b/va_bench/recipes/rf100vl/dfine.json @@ -28,6 +28,7 @@ "x": {"imgsz": 640} }, "train": { + "save_period": 0, "optimizer": "adamw", "lr0": 0.0002, "weight_decay": 0.0001, diff --git a/va_bench/recipes/rf100vl/ec.json b/va_bench/recipes/rf100vl/ec.json index c8ceca5..65d2052 100644 --- a/va_bench/recipes/rf100vl/ec.json +++ b/va_bench/recipes/rf100vl/ec.json @@ -27,6 +27,7 @@ "x": {"imgsz": 640, "lr0": 0.0005, "backbone_lr_mult": 0.01} }, "train": { + "save_period": 0, "optimizer": "adamw", "weight_decay": 0.0001, "scheduler": "flat_cosine", diff --git a/va_bench/recipes/rf100vl/picodet.json b/va_bench/recipes/rf100vl/picodet.json index fd03a18..12f931a 100644 --- a/va_bench/recipes/rf100vl/picodet.json +++ b/va_bench/recipes/rf100vl/picodet.json @@ -26,6 +26,7 @@ "l": {"imgsz": 640} }, "train": { + "save_period": 0, "optimizer": "sgd", "lr0": 0.01, "momentum": 0.9, diff --git a/va_bench/recipes/rf100vl/rfdetr.json b/va_bench/recipes/rf100vl/rfdetr.json index 514efab..8054568 100644 --- a/va_bench/recipes/rf100vl/rfdetr.json +++ b/va_bench/recipes/rf100vl/rfdetr.json @@ -35,6 +35,7 @@ } }, "train": { + "save_period": 0, "optimizer": "adamw", "lr0": 0.0001, "weight_decay": 0.0001, diff --git a/va_bench/recipes/rf100vl/rtdetr.json b/va_bench/recipes/rf100vl/rtdetr.json index 1585a24..6f3348a 100644 --- a/va_bench/recipes/rf100vl/rtdetr.json +++ b/va_bench/recipes/rf100vl/rtdetr.json @@ -21,6 +21,7 @@ }, "sizes": {}, "train": { + "save_period": 0, "imgsz": 640, "optimizer": "adamw", "lr0": 0.0001, diff --git a/va_bench/recipes/rf100vl/rtdetrv2.json b/va_bench/recipes/rf100vl/rtdetrv2.json index 766e9eb..1f0c074 100644 --- a/va_bench/recipes/rf100vl/rtdetrv2.json +++ b/va_bench/recipes/rf100vl/rtdetrv2.json @@ -22,6 +22,7 @@ }, "sizes": {}, "train": { + "save_period": 0, "imgsz": 640, "optimizer": "adamw", "lr0": 0.0001, diff --git a/va_bench/recipes/rf100vl/rtdetrv4.json b/va_bench/recipes/rf100vl/rtdetrv4.json index 000b111..06dcb31 100644 --- a/va_bench/recipes/rf100vl/rtdetrv4.json +++ b/va_bench/recipes/rf100vl/rtdetrv4.json @@ -21,6 +21,7 @@ }, "sizes": {}, "train": { + "save_period": 0, "imgsz": 640, "optimizer": "adamw", "lr0": 0.0005, diff --git a/va_bench/recipes/rf100vl/rtmdet.json b/va_bench/recipes/rf100vl/rtmdet.json index 90e8ddf..332f8dc 100644 --- a/va_bench/recipes/rf100vl/rtmdet.json +++ b/va_bench/recipes/rf100vl/rtmdet.json @@ -22,6 +22,7 @@ }, "sizes": {}, "train": { + "save_period": 0, "imgsz": 640, "optimizer": "adamw", "lr0": 0.004, diff --git a/va_bench/recipes/rf100vl/yolonas.json b/va_bench/recipes/rf100vl/yolonas.json index a271d48..7333f81 100644 --- a/va_bench/recipes/rf100vl/yolonas.json +++ b/va_bench/recipes/rf100vl/yolonas.json @@ -26,6 +26,7 @@ "l": {"imgsz": 640, "lr0": 0.0005} }, "train": { + "save_period": 0, "optimizer": "adamw", "momentum": 0.9, "weight_decay": 0.00001, diff --git a/va_bench/recipes/rf100vl/yolov7.json b/va_bench/recipes/rf100vl/yolov7.json index c865d21..d352f26 100644 --- a/va_bench/recipes/rf100vl/yolov7.json +++ b/va_bench/recipes/rf100vl/yolov7.json @@ -24,6 +24,7 @@ "b": {"imgsz": 640, "lr0": 0.01} }, "train": { + "save_period": 0, "optimizer": "sgd", "momentum": 0.937, "weight_decay": 0.0005, diff --git a/va_bench/recipes/rf100vl/yolov9-e2e.json b/va_bench/recipes/rf100vl/yolov9-e2e.json index da8d055..5322692 100644 --- a/va_bench/recipes/rf100vl/yolov9-e2e.json +++ b/va_bench/recipes/rf100vl/yolov9-e2e.json @@ -31,6 +31,7 @@ "c": {"imgsz": 640, "lr0": 0.01} }, "train": { + "save_period": 0, "optimizer": "sgd", "momentum": 0.937, "weight_decay": 0.0005, diff --git a/va_bench/recipes/rf100vl/yolov9.json b/va_bench/recipes/rf100vl/yolov9.json index 4f53337..a1723b3 100644 --- a/va_bench/recipes/rf100vl/yolov9.json +++ b/va_bench/recipes/rf100vl/yolov9.json @@ -28,6 +28,7 @@ "c": {"imgsz": 640, "lr0": 0.01} }, "train": { + "save_period": 0, "optimizer": "sgd", "momentum": 0.937, "weight_decay": 0.0005, diff --git a/va_bench/recipes/rf100vl/yolox.json b/va_bench/recipes/rf100vl/yolox.json index 66c3665..5c5e779 100644 --- a/va_bench/recipes/rf100vl/yolox.json +++ b/va_bench/recipes/rf100vl/yolox.json @@ -29,6 +29,7 @@ "x": {"imgsz": 640, "lr0": 0.01} }, "train": { + "save_period": 0, "optimizer": "sgd", "momentum": 0.937, "weight_decay": 0.0005,