From 6b59a8d54f2ff11cd3082154fe7cdb2e202983ca Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Sun, 21 Jun 2026 12:59:50 +0200 Subject: [PATCH] feat: pool rebuild_manifest recovery Co-Authored-By: Claude Opus 4.8 --- gates/pool.py | 18 ++++++++++++++++-- tests/test_pool.py | 22 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/gates/pool.py b/gates/pool.py index 64bb7ff..8fab0b5 100644 --- a/gates/pool.py +++ b/gates/pool.py @@ -1,6 +1,7 @@ """Pure storage layer for the Image Pool node. Stdlib only — no torch, no comfy.""" import json import os +import re from pathlib import Path @@ -112,5 +113,18 @@ def set_label(base_dir, pool_id, index, label): def rebuild_manifest(base_dir, pool_id): - # Temporary stub — replaced in Task 7. - return empty_manifest() + d = pool_dir(base_dir, pool_id) + m = empty_manifest() + if not d.exists(): + return m + imgs = sorted(p.name for p in d.glob("img_*.png") if not p.name.endswith(".mask.png")) + max_seq = 0 + for name in imgs: + match = re.match(r"img_(\d+)\.png$", name) + seq = int(match.group(1)) if match else 0 + max_seq = max(max_seq, seq) + mask_name = name.replace(".png", ".mask.png") + mask = mask_name if (d / mask_name).exists() else None + m["slots"].append({"image": name, "mask": mask, "label": "", "added": 0}) + m["next_seq"] = max_seq + 1 + return m diff --git a/tests/test_pool.py b/tests/test_pool.py index c1539da..ddcd36b 100644 --- a/tests/test_pool.py +++ b/tests/test_pool.py @@ -97,3 +97,25 @@ def test_set_label_out_of_range_noop(tmp_path): pool.add_image(str(tmp_path), "p1", b"a", ts=1) m = pool.set_label(str(tmp_path), "p1", 5, "x") assert m["slots"][0]["label"] == "" + + +def test_rebuild_from_files(tmp_path): + d = tmp_path / "p1" + d.mkdir() + (d / "img_0001.png").write_bytes(b"a") + (d / "img_0001.mask.png").write_bytes(b"m") + (d / "img_0003.png").write_bytes(b"c") # gap on purpose + m = pool.rebuild_manifest(str(tmp_path), "p1") + assert [s["image"] for s in m["slots"]] == ["img_0001.png", "img_0003.png"] + assert m["slots"][0]["mask"] == "img_0001.mask.png" + assert m["slots"][1]["mask"] is None + assert m["next_seq"] == 4 # max seq 3 + 1 + assert m["active"] == 0 + + +def test_read_corrupt_manifest_triggers_rebuild(tmp_path): + d = tmp_path / "p1"; d.mkdir() + (d / "img_0001.png").write_bytes(b"a") + (d / "manifest.json").write_text("{ not json") + m = pool.read_manifest(str(tmp_path), "p1") + assert [s["image"] for s in m["slots"]] == ["img_0001.png"]