← back to Designer Wallcoverings

onboarding/sangetsu-lilycolor/scripts/enrich-local-supervised.sh

81 lines

#!/usr/bin/env bash
# enrich-local-supervised.sh — THE single canonical Lily recovery runner.
#
# Combines: (1) supervise.sh's jetsam-survival respawn loop, and (2) the local
# cache + good-seed + remerge logic. Reads images from the STABLE local SSD
# cache (.image-cache/lily), NOT flaky /Volumes/Henry — so image_not_found under
# sustained I/O can't happen. Resumable, $0 local, JSONL-only (reversible).
#
# Completion = a clean enrich pass (rc==0) that produced NO new rows.
set -u
PROJ="$HOME/Projects/Designer-Wallcoverings/onboarding/sangetsu-lilycolor"
cd "$PROJ" || exit 1
ST="$PROJ/staging"; CACHE="$PROJ/.image-cache/lily"
FINAL="$ST/enrichment-full-063026A.final.jsonl"
GOOD="$ST/enrichment-full-063026A.good-seed2.jsonl"
REC="$ST/enrichment-full-063026A.local-recovery.jsonl"
LOG="$ST/enrich-localsup.log"
log(){ echo "[localsup $(date '+%H:%M:%S')] $*" >> "$LOG"; }

[ -d "$CACHE" ] || { log "FATAL cache missing $CACHE"; exit 1; }
NC=$(find "$CACHE" -type f 2>/dev/null | wc -l | tr -d ' ')
log "START local-supervised recovery. cache files=$NC"

# good-seed2 = current final rows WITHOUT error and not usable:false
python3 - "$FINAL" "$GOOD" <<'PY' 2>> "$LOG"
import json,sys
src,good=sys.argv[1],sys.argv[2]; bad=0; ok=0
with open(good,"w") as g:
    for line in open(src):
        line=line.strip()
        if not line: continue
        try:o=json.loads(line)
        except:continue
        if o.get("error") or o.get("usable") is False: bad+=1; continue
        ok+=1; g.write(json.dumps(o)+"\n")
print(f"[seed] good={ok} to-redo={bad}")
PY

count(){ wc -l < "$REC" 2>/dev/null | tr -d ' ' || echo 0; }
# supervise loop: relaunch on any jetsam/non-clean exit; stop on clean+no-new
while true; do
  before="$(count)"
  ENRICH_HENRY_DIR="$CACHE" ENRICH_SHARD_COUNT=1 ENRICH_OLLAMA_URL=http://127.0.0.1:11434 \
    ENRICH_DONE_SEEDS="$GOOD" python3 -u scripts/enrich-full.py "$REC" >> "$LOG" 2>&1
  rc=$?; after="$(count)"
  if [ "$rc" -eq 0 ] && [ "$before" = "$after" ]; then
    log "COMPLETE (clean exit, no new rows at ${after})"; break
  fi
  log "pass rc=$rc rows ${before}->${after} — relaunch in 5s"; sleep 5
done

# Remerge ALL enrichment sources (last-wins) into final so nothing is lost.
python3 - "$FINAL" <<'PY' 2>> "$LOG"
import json,sys,glob,os
ST=os.path.dirname(sys.argv[1]); final=sys.argv[1]
srcs=[final, os.path.join(ST,"enrichment-full-063026A.good-seed2.jsonl"),
      os.path.join(ST,"enrichment-full-063026A.local-recovery.jsonl"),
      os.path.join(ST,"enrichment-full-063026A.local-recover.jsonl"),
      os.path.join(ST,"enrichment-full-063026A.jsonl")]
by={}
def better(new,old):
    # prefer a real enrichment row over an error/unusable one
    def score(o): return 0 if (o.get("error") or o.get("usable") is False) else 1
    return score(new) >= score(old)
for p in srcs:
    if not os.path.exists(p): continue
    for line in open(p):
        line=line.strip()
        if not line: continue
        try:o=json.loads(line)
        except:continue
        s=o.get("sku")
        if not s: continue
        if s not in by or better(o,by[s]): by[s]=o
ok=sum(1 for o in by.values() if not o.get("error") and o.get("usable") is not False)
with open(final,"w") as f:
    for s in sorted(by): f.write(json.dumps(by[s])+"\n")
print(f"[remerge] final={len(by)} ok={ok} err={len(by)-ok}")
PY
log "REMERGE done. final=$(wc -l < "$FINAL") rows"; touch "$ST/LOCALRE-DONE"; log "ALL DONE"