[object Object]

← back to Wallco Ai

seam-wave: build manifest — 3 animal-forward FAIL roots (43199/54385/55272)

04386639c6beb132257268fdc0989b4a08bfc4f3 · 2026-06-11 19:02:41 -0700 · Steve Abrams

Scanned 2448 eligible published drunk-animals roots (not in curator's 85, not
retired) with the canonical both-axes gate verify-edge-seamless.py: 848 PASS /
1586 WARN / 7 distinct FAIL. Animal-forward pre-filter (free local llava) kept
3 (macaw/peacock/elephant), dropped 4 (2 pastoral toile, 1 animal-minority,
1 glassware-dominant). Adds scripts/seam-fail-scan.py (read-only scanner).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Files touched

Diff

commit 04386639c6beb132257268fdc0989b4a08bfc4f3
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Jun 11 19:02:41 2026 -0700

    seam-wave: build manifest — 3 animal-forward FAIL roots (43199/54385/55272)
    
    Scanned 2448 eligible published drunk-animals roots (not in curator's 85, not
    retired) with the canonical both-axes gate verify-edge-seamless.py: 848 PASS /
    1586 WARN / 7 distinct FAIL. Animal-forward pre-filter (free local llava) kept
    3 (macaw/peacock/elephant), dropped 4 (2 pastoral toile, 1 animal-minority,
    1 glassware-dominant). Adds scripts/seam-fail-scan.py (read-only scanner).
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
 data/seam-fail-scan.json  |  84 +++++++++++++++++++++++++++++
 data/seam-wave-roots.txt  |  27 ++++++----
 scripts/seam-fail-scan.py | 134 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 236 insertions(+), 9 deletions(-)

diff --git a/data/seam-fail-scan.json b/data/seam-fail-scan.json
new file mode 100644
index 0000000..fd0f4d4
--- /dev/null
+++ b/data/seam-fail-scan.json
@@ -0,0 +1,84 @@
+{
+  "eligible_after_skip": 2448,
+  "skipped_in_queue_or_excluded": 85,
+  "tally": {
+    "PASS": 848,
+    "WARN": 1586,
+    "FAIL": 14,
+    "MISSING": 0,
+    "ERROR": 0
+  },
+  "fail_count": 14,
+  "fails": [
+    {
+      "id": 43197,
+      "overall_max": 38.77,
+      "path": "/Users/stevestudio2/Projects/wallco-ai/data/generated/1779805176441_993290578.png"
+    },
+    {
+      "id": 43197,
+      "overall_max": 38.77,
+      "path": "/Users/stevestudio2/Projects/wallco-ai/data/generated/1779805176441_993290578.png"
+    },
+    {
+      "id": 50566,
+      "overall_max": 36.36,
+      "path": "/Users/stevestudio2/Projects/wallco-ai/data/generated/1779811502549_1280451696.png"
+    },
+    {
+      "id": 50566,
+      "overall_max": 36.36,
+      "path": "/Users/stevestudio2/Projects/wallco-ai/data/generated/1779811502549_1280451696.png"
+    },
+    {
+      "id": 50565,
+      "overall_max": 35.01,
+      "path": "/Users/stevestudio2/Projects/wallco-ai/data/generated/1779810576790_159605057.png"
+    },
+    {
+      "id": 50565,
+      "overall_max": 35.01,
+      "path": "/Users/stevestudio2/Projects/wallco-ai/data/generated/1779810576790_159605057.png"
+    },
+    {
+      "id": 43199,
+      "overall_max": 20.13,
+      "path": "/Users/stevestudio2/Projects/wallco-ai/data/generated/1779806462956_732266097.png"
+    },
+    {
+      "id": 43199,
+      "overall_max": 20.13,
+      "path": "/Users/stevestudio2/Projects/wallco-ai/data/generated/1779806462956_732266097.png"
+    },
+    {
+      "id": 55272,
+      "overall_max": 16.14,
+      "path": "/Users/stevestudio2/Projects/wallco-ai/data/generated/1780336621951_1793762070.png"
+    },
+    {
+      "id": 55272,
+      "overall_max": 16.14,
+      "path": "/Users/stevestudio2/Projects/wallco-ai/data/generated/1780336621951_1793762070.png"
+    },
+    {
+      "id": 54686,
+      "overall_max": 16.01,
+      "path": "/Users/stevestudio2/Projects/wallco-ai/data/generated/1780181582926_458477544.png"
+    },
+    {
+      "id": 54686,
+      "overall_max": 16.01,
+      "path": "/Users/stevestudio2/Projects/wallco-ai/data/generated/1780181582926_458477544.png"
+    },
+    {
+      "id": 54385,
+      "overall_max": 13.16,
+      "path": "/Users/stevestudio2/Projects/wallco-ai/data/generated/1780088222096_113927942.png"
+    },
+    {
+      "id": 54385,
+      "overall_max": 13.16,
+      "path": "/Users/stevestudio2/Projects/wallco-ai/data/generated/1780088222096_113927942.png"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/data/seam-wave-roots.txt b/data/seam-wave-roots.txt
index 52ea12a..54a23d2 100644
--- a/data/seam-wave-roots.txt
+++ b/data/seam-wave-roots.txt
@@ -1,13 +1,22 @@
-# seam-wave-roots.txt — the remaining flagged roots to seam-fix, one id per line.
+# seam-wave-roots.txt — the remaining ANIMAL-FORWARD FAIL roots to seam-fix.
 # Consumed by scripts/seam-wave.sh in waves of WAVE_SIZE (default 20).
 # Lines starting with '#' and blank lines are ignored.
 #
-# HOW TO POPULATE (next session, after the 5-root sample is curated + approved):
-#   The 85 roots already in the seam-fix curator each have a PASS candidate — they
-#   are DONE. This manifest is for the remaining FAIL roots NOT yet processed.
-#   Generate the list from a seam scan of published roots, e.g.:
-#       python3 scripts/edges-batch-scan.py  # or fuzzy_seam_scan.py / seam_break_scan.py
-#   then write the FAIL root ids here (one per line), excluding any root that already
-#   has a PASS-both-axes candidate in data/seam-fix-queue.jsonl.
+# BUILT 2026-06-11 by scripts/seam-fail-scan.py + the local-llava animal-dominance
+# pre-filter (scripts/vision-contains-local.py). Method:
+#   1. Scanned the 2448 eligible published drunk-animals roots that are NOT already
+#      in the seam-fix curator (85 queue roots) and NOT retired (54076/54266) with
+#      the canonical both-axes gate scripts/verify-edge-seamless.py.
+#      → 848 PASS · 1586 WARN · 14 FAIL rows = 7 DISTINCT FAIL roots.
+#   2. Animal-forward pre-filter (free local llava): "is a single animal the largest,
+#      most dominant element?" — drops glassware-dominant / animal-minority roots
+#      the ControlNet pipeline can't rescue.
+#      KEEP (animal-forward): 43199 macaw · 54385 peacock · 55272 elephant
+#      DROP: 43197 / 50565 pastoral toile (no dominant animal); 50566 busy pastoral
+#            (animal-minority); 54686 glassware-dominant (mai-tai/hurricane glass).
 #
-# Until this file has real ids below, scripts/seam-wave.sh GATE 2 will refuse to run.
+# Result: 3 roots = 1 wave (WAVE_SIZE=20). Most of the original FAIL universe was
+# already swept into the curator's 85; the strict both-axes gate rates the rest WARN.
+43199
+54385
+55272
diff --git a/scripts/seam-fail-scan.py b/scripts/seam-fail-scan.py
new file mode 100644
index 0000000..9a22e6b
--- /dev/null
+++ b/scripts/seam-fail-scan.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+"""seam-fail-scan.py — read-only seam scan over eligible published drunk-animals
+roots, to (re)build the remaining-FAIL universe for the seam-wave manifest.
+
+Mirrors the scan that produced the 392-FAIL / 80-worst-FAIL curator seed
+(commit 0236a2b): the eligible universe is published, parent NULL, has a
+local_path that resolves on disk, not user_removed, needs_fixing IS NULL,
+category=drunk-animals. Each resolved PNG is run through the canonical
+true-toroidal both-axes gate (verify-edge-seamless.verify_path). We tally
+PASS/WARN/FAIL/MISSING and emit the FAIL root ids (with overall_max for ranking).
+
+Excludes (so the output is the REMAINING work):
+  • roots already present in data/seam-fix-queue.jsonl (the ~85 in the curator —
+    DONE per the runbook), and
+  • roots in data/seam-wave-exclude.txt (retired glassware-dominant).
+
+Writes data/seam-fail-scan.json = {tally, fails:[{id,overall_max,path}], ...}.
+DOES NOT touch the DB beyond a single read, never flips is_published, no deploy.
+
+Usage: python3 scripts/seam-fail-scan.py
+"""
+import json, os, sys
+from pathlib import Path
+
+ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+GEN = os.path.join(ROOT, 'data', 'generated')
+QUEUE = os.path.join(ROOT, 'data', 'seam-fix-queue.jsonl')
+EXCLUDE = os.path.join(ROOT, 'data', 'seam-wave-exclude.txt')
+OUT = os.path.join(ROOT, 'data', 'seam-fail-scan.json')
+
+sys.path.insert(0, os.path.join(ROOT, 'scripts'))
+from importlib import import_module
+sfv = import_module('seam-fix-variants')
+edgeverify = import_module('verify-edge-seamless')
+
+
+def resolve(lp):
+    if not lp:
+        return None
+    cands = [lp]
+    if not lp.endswith('.png'):
+        cands.append(lp + '.png')
+    base = os.path.basename(lp.rstrip('/'))
+    cands += [os.path.join(GEN, base),
+              os.path.join(GEN, base + '.png'),
+              os.path.join(GEN, base, 'final.png')]
+    return next((c for c in cands if os.path.isfile(c)), None)
+
+
+def queue_roots():
+    s = set()
+    if os.path.exists(QUEUE):
+        for line in open(QUEUE):
+            line = line.strip()
+            if not line:
+                continue
+            try:
+                r = json.loads(line)
+            except Exception:
+                continue
+            if r.get('root_id') is not None:
+                s.add(int(r['root_id']))
+    return s
+
+
+def excluded_roots():
+    s = set()
+    if os.path.exists(EXCLUDE):
+        for line in open(EXCLUDE):
+            line = line.split('#', 1)[0].strip()
+            if line.isdigit():
+                s.add(int(line))
+    return s
+
+
+def main():
+    skip = queue_roots() | excluded_roots()
+    out = sfv.psql('''
+SELECT id, COALESCE(local_path,'')
+FROM all_designs
+WHERE is_published=true
+  AND COALESCE(user_removed,false)=false
+  AND parent_design_id IS NULL
+  AND needs_fixing_at IS NULL
+  AND category='drunk-animals'
+  AND local_path IS NOT NULL AND local_path<>''
+ORDER BY id;
+''')
+    rows = []
+    for line in out.splitlines():
+        if '|' not in line:
+            continue
+        sid, lp = line.split('|', 1)
+        try:
+            rid = int(sid)
+        except ValueError:
+            continue
+        if rid in skip:
+            continue
+        rows.append((rid, lp))
+
+    tally = {'PASS': 0, 'WARN': 0, 'FAIL': 0, 'MISSING': 0, 'ERROR': 0}
+    fails = []
+    total = len(rows)
+    for i, (rid, lp) in enumerate(rows):
+        path = resolve(lp)
+        if not path:
+            tally['MISSING'] += 1
+            continue
+        try:
+            ev = edgeverify.verify_path(Path(path))
+        except Exception:
+            tally['ERROR'] += 1
+            continue
+        v = ev.get('verdict', 'ERROR')
+        tally[v] = tally.get(v, 0) + 1
+        if v == 'FAIL':
+            ax = ev.get('axes', {})
+            hm = (ax.get('horizontal') or {}).get('best', 0) or 0
+            vm = (ax.get('vertical') or {}).get('best', 0) or 0
+            fails.append({'id': rid, 'overall_max': round(max(hm, vm), 2), 'path': path})
+        if (i + 1) % 100 == 0:
+            print(f"  scanned {i+1}/{total}  tally={tally}", file=sys.stderr)
+
+    fails.sort(key=lambda r: -r['overall_max'])
+    report = {'eligible_after_skip': total, 'skipped_in_queue_or_excluded': len(skip),
+              'tally': tally, 'fail_count': len(fails), 'fails': fails}
+    with open(OUT, 'w') as f:
+        json.dump(report, f, indent=2)
+    print(json.dumps({'tally': tally, 'fail_count': len(fails), 'out': OUT}, indent=2))
+
+
+if __name__ == '__main__':
+    main()

← f1efeda Add autonomous overnight seam-fix wave runner (DTD-gated, se  ·  back to Wallco Ai  ·  seam-wave: generalize the retry-loop generator to arbitrary 2fed439 →