← back to Designer Wallcoverings
enrich-full.py: treat mid-run Henry unmount as transient (VolumeUnavailable -> retry/defer) instead of burning SKUs as permanent image_not_found stubs (a memory-pressure I/O stall burned 1992 this way)
dcd19e579e2c11435278f44ec2142a83cfc344b4 · 2026-07-01 14:14:47 -0700 · Steve
Files touched
M onboarding/sangetsu-lilycolor/scripts/enrich-full.py
Diff
commit dcd19e579e2c11435278f44ec2142a83cfc344b4
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Jul 1 14:14:47 2026 -0700
enrich-full.py: treat mid-run Henry unmount as transient (VolumeUnavailable -> retry/defer) instead of burning SKUs as permanent image_not_found stubs (a memory-pressure I/O stall burned 1992 this way)
---
onboarding/sangetsu-lilycolor/scripts/enrich-full.py | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/onboarding/sangetsu-lilycolor/scripts/enrich-full.py b/onboarding/sangetsu-lilycolor/scripts/enrich-full.py
index 785b5886..bf20bc6c 100644
--- a/onboarding/sangetsu-lilycolor/scripts/enrich-full.py
+++ b/onboarding/sangetsu-lilycolor/scripts/enrich-full.py
@@ -106,8 +106,19 @@ def clean(arr, cap=4):
return out
+class VolumeUnavailable(Exception):
+ """The image's source volume (e.g. Henry) is not mounted — an INFRASTRUCTURE
+ outage, not a missing file. Routed through the transient retry/defer path so a
+ mid-run drive drop never burns a SKU as a permanent image_not_found stub."""
+
+
def enrich_one(sku, img):
if not os.path.exists(img):
+ # If the containing directory is gone, the whole volume dropped (Henry
+ # unmounted) → transient, defer. If the dir is present but the file is
+ # absent, it's a genuinely missing image → permanent stub.
+ if not os.path.isdir(os.path.dirname(img)):
+ raise VolumeUnavailable(f"volume for {img} unavailable")
return {"sku": sku, "error": "image_not_found"}
t0 = time.time()
samp = palette(img, 6)
@@ -258,8 +269,10 @@ def main():
fout.write(json.dumps(row, ensure_ascii=False) + "\n")
fout.flush()
break
- except (urllib.error.URLError, TimeoutError, ConnectionError) as e:
- # TRANSIENT ollama outage (Errno 61 Connection refused / timeout).
+ except (urllib.error.URLError, TimeoutError, ConnectionError,
+ VolumeUnavailable) as e:
+ # TRANSIENT outage — ollama down (Errno 61 / timeout) OR the
+ # image volume (Henry) unmounted mid-run.
# NEVER write a stub: a stub row carries a "sku" key, which load_done
# would treat as done and skip forever. Wait for ollama to return and
# retry the SAME SKU with capped backoff; if it stays down past the
@@ -271,7 +284,7 @@ def main():
f"(ollama still down) — left for a later sweep", flush=True)
break
wait = min(60, 5 * attempt)
- print(f"[{i}/{len(todo)}] {sku} ollama down ({str(e)[:60]}) — "
+ print(f"[{i}/{len(todo)}] {sku} transient outage ({str(e)[:60]}) — "
f"retry {attempt}/{MAX_TRANSIENT_RETRIES} in {wait}s", flush=True)
time.sleep(wait)
continue
← db33be40 enrich-full.py: retry-with-backoff on transient ollama outag
·
back to Designer Wallcoverings
·
recover: enrich Lily swatches from local copy (Henry USB unm 324313fd →