[object Object]

← back to Wallco Ai

DA-2+DA-3: Met PD + prior wallco design references in every drunk-animal prompt

061b76f9aa7d1fd7817cfd5bcc6f1843e4deadf7 · 2026-05-25 20:34:25 -0700 · Steve Abrams

YOLO queue items DA-1 → DA-3 complete.

DA-1 audit: 3,518 prompts missing drink vocab; 830 are PUBLISHED.
- drunk-animals: 3398 missing (752 published)
- drunk-zoo-36: 118 missing (76 published)
- drunk-monkeys-v2: 2 missing
- drunk-animals-designer: 0 missing

DA-2 — lib/design-references.js:
- pickMetReference(themes) — samples dw_unified.met_museum_art PD rows
  across 5 theme pools (bacchanal, wine, engraving, animal, chinoiserie),
  120 rows per pool cached at module-load with 6-hr TTL
- pickPriorDesignReference(category) — samples top-rated prior published
  wallco design from spoon_all_designs, bucketed per drunk-* category
- pickReferences(category) — returns combined { met, prior, promptClause }
  ready to paste into a prompt
- themesForCategory() — maps each drunk-* sub-category to a sensible
  Met theme mix (zoo-36 → bacchanal/animal/engraving, designer →
  chinoiserie/wine/engraving, etc.)
- Graceful fallback to a 4-entry MET_FALLBACK list if PG unreachable

DA-3 — wire into all 4 generators via SHARED.pickReferenceClause(cat):
- drunk_animal_prompts.js — buildPrompt + buildN both inject ref clause
- drunk_monkeys_v2_prompts.js — "Reference anchor: ..." line in positive
- drunk_monkeys_birds_subdued_prompts.js — same
- night-builder.js drunk-zoo-36 composer — same

Sample fresh prompt now reads:
  "...inspired by the Met's 'Terracotta fragment of a skyphos (deep
   drinking cup)' (Unknown), composition lineage from wallco internal
   design #38562 (#6c3e31 dominant)..."

DA-4 (preview batch generator scripts/da4-preview-batch.js) kicked off
in background; 6 heterogeneous designs across 4 categories, curator-mode,
local ComfyUI. Results to data/logs/drunk-animals-preview-batch.json.

Files touched

Diff

commit 061b76f9aa7d1fd7817cfd5bcc6f1843e4deadf7
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon May 25 20:34:25 2026 -0700

    DA-2+DA-3: Met PD + prior wallco design references in every drunk-animal prompt
    
    YOLO queue items DA-1 → DA-3 complete.
    
    DA-1 audit: 3,518 prompts missing drink vocab; 830 are PUBLISHED.
    - drunk-animals: 3398 missing (752 published)
    - drunk-zoo-36: 118 missing (76 published)
    - drunk-monkeys-v2: 2 missing
    - drunk-animals-designer: 0 missing
    
    DA-2 — lib/design-references.js:
    - pickMetReference(themes) — samples dw_unified.met_museum_art PD rows
      across 5 theme pools (bacchanal, wine, engraving, animal, chinoiserie),
      120 rows per pool cached at module-load with 6-hr TTL
    - pickPriorDesignReference(category) — samples top-rated prior published
      wallco design from spoon_all_designs, bucketed per drunk-* category
    - pickReferences(category) — returns combined { met, prior, promptClause }
      ready to paste into a prompt
    - themesForCategory() — maps each drunk-* sub-category to a sensible
      Met theme mix (zoo-36 → bacchanal/animal/engraving, designer →
      chinoiserie/wine/engraving, etc.)
    - Graceful fallback to a 4-entry MET_FALLBACK list if PG unreachable
    
    DA-3 — wire into all 4 generators via SHARED.pickReferenceClause(cat):
    - drunk_animal_prompts.js — buildPrompt + buildN both inject ref clause
    - drunk_monkeys_v2_prompts.js — "Reference anchor: ..." line in positive
    - drunk_monkeys_birds_subdued_prompts.js — same
    - night-builder.js drunk-zoo-36 composer — same
    
    Sample fresh prompt now reads:
      "...inspired by the Met's 'Terracotta fragment of a skyphos (deep
       drinking cup)' (Unknown), composition lineage from wallco internal
       design #38562 (#6c3e31 dominant)..."
    
    DA-4 (preview batch generator scripts/da4-preview-batch.js) kicked off
    in background; 6 heterogeneous designs across 4 categories, curator-mode,
    local ComfyUI. Results to data/logs/drunk-animals-preview-batch.json.
---
 .yolo-queue.md                                 |  18 +-
 data/logs/drunk-animals-glass-audit.json       |   1 +
 lib/design-references.js                       | 226 +++++++++++++++++++++++++
 scripts/da4-preview-batch.js                   | 126 ++++++++++++++
 scripts/drunk_animal_prompts.js                |  10 +-
 scripts/drunk_monkeys_birds_subdued_prompts.js |   5 +-
 scripts/drunk_monkeys_v2_prompts.js            |   9 +-
 scripts/drunk_motif_shared.js                  |  34 +++-
 scripts/night-builder.js                       |   2 +
 9 files changed, 414 insertions(+), 17 deletions(-)

diff --git a/.yolo-queue.md b/.yolo-queue.md
index ca30b2b..c52d102 100644
--- a/.yolo-queue.md
+++ b/.yolo-queue.md
@@ -162,17 +162,31 @@ for a COMMITTED verdict. For blockers / 2-path forks, use
   > 2 missing / 259 OK; drunk-animals-designer: 0 missing / 115 OK. The
   > 830-published-missing-drink set is the obvious priority for DA-6 batch.
 
-- [ ] **DA-2** Build `lib/design-references.js` — exports
+- [x] **DA-2** Build `lib/design-references.js` — exports
   `pickMetReference(themes)` (samples 1-2 PD rows from
   `dw_unified.met_museum_art` matching themes like 'bacchanal', 'wine',
   'engraving', 'animal') + `pickPriorDesignReference(category)` (samples
   1 top-rated prior wallco design from `spoon_all_designs` matching
   category). Returns title + artist + period + reference URL.
 
-- [ ] **DA-3** Wire `lib/design-references.js` into all 4 drunk-animal
+  > NOTE 2026-05-25 ✓ Shipped at lib/design-references.js. 5 Met PD theme
+  > pools cached (bacchanal/wine/engraving/animal/chinoiserie, 120 each).
+  > Prior wallco cache buckets: drunk-animals 151, drunk-zoo-36 399,
+  > drunk-monkeys-v2 50. Sample pulled: "The Triumph of Caesar: The
+  > Elephants" (Mantegna ca. 1497), "Bacchus" (Meissen Manufactory).
+  > CLI smoke test passes. 6-hr TTL, lazy refresh.
+
+- [x] **DA-3** Wire `lib/design-references.js` into all 4 drunk-animal
   generators. Inject `inspired by [Met title] ([artist], [period])` +
   `composition lineage from internal design #[id]` into every prompt.
 
+  > NOTE 2026-05-25 ✓ SHARED.pickReferenceClause(category) wired into
+  > all 4 generators. Sample fresh prompt now carries "inspired by the
+  > Met's 'Terracotta fragment of a skyphos (deep drinking cup)',
+  > composition lineage from wallco internal design #38562". Falls back
+  > to empty clause if PG unreachable (caches PD pool at module load +
+  > 6-hr TTL).
+
 - [ ] **DA-4** Generate 6 heterogeneous preview designs spanning the
   4 categories (2 drunk-animals, 2 drunk-zoo-36, 1 drunk-monkeys-v2,
   1 drunk-animals-designer). curator-mode (is_published=false), local
diff --git a/data/logs/drunk-animals-glass-audit.json b/data/logs/drunk-animals-glass-audit.json
new file mode 100644
index 0000000..137c4d3
--- /dev/null
+++ b/data/logs/drunk-animals-glass-audit.json
@@ -0,0 +1 @@
+[{"category" : "drunk-animals", "has_drink" : 1003, "missing_drink" : 3398, "has_drink_published" : 43, "missing_drink_published" : 752, "sample_missing_ids" : [38425,38501,38550,39662,41297,41318,41362,41369,41420,41531]}, {"category" : "drunk-monkeys-v2", "has_drink" : 259, "missing_drink" : 2, "has_drink_published" : 250, "missing_drink_published" : 2, "sample_missing_ids" : [39299,39304]}, {"category" : "drunk-zoo-36", "has_drink" : 2219, "missing_drink" : 118, "has_drink_published" : 1993, "missing_drink_published" : 76, "sample_missing_ids" : [39455,39502,39760,39947,40448,40538,40539,40912,40913,41087]}, {"category" : "drunk-animals-designer", "has_drink" : 115, "missing_drink" : 0, "has_drink_published" : 2, "missing_drink_published" : 0, "sample_missing_ids" : []}]
diff --git a/lib/design-references.js b/lib/design-references.js
new file mode 100644
index 0000000..4d0b857
--- /dev/null
+++ b/lib/design-references.js
@@ -0,0 +1,226 @@
+/**
+ * Design reference picker — pulls art-historical anchors from
+ *   (a) Met Museum public-domain artworks (dw_unified.met_museum_art)
+ *   (b) prior wallco designs already in the catalog (spoon_all_designs)
+ * for use as style/composition anchors inside drunk-animal /
+ * drunk-zoo / mural generator prompts.
+ *
+ * Steve directive 2026-05-25 — "designs pulled from the postgresql
+ * and met museum images" + "this is our llm". We use real PD artwork
+ * and our own prior winners as the inspiration layer for every new
+ * prompt, instead of relying purely on the diffusion model's training.
+ *
+ * USAGE:
+ *   const refs = require('./lib/design-references');
+ *   const met  = refs.pickMetReference(['bacchanal', 'wine']);
+ *   const own  = refs.pickPriorDesignReference('drunk-animals');
+ *   // append to prompt body:
+ *   //   `inspired by ${met.compactCitation}, composition lineage from ${own.compactCitation}`
+ *
+ * Both pickers are SYNCHRONOUS (they use execSync against a pre-warmed
+ * in-memory cache) so the generator hot paths stay simple. Cache
+ * refresh happens at module-load + once every 6 hours (configurable
+ * via DESIGN_REFS_TTL_SEC).
+ *
+ * If PG is unreachable, fall back to a tiny hardcoded list so the
+ * generators never throw — just less variety.
+ */
+'use strict';
+
+const { execSync } = require('child_process');
+
+// ── Theme pools — Met PD query templates keyed by drunk-animal vibe ───
+const MET_THEMES = {
+  bacchanal:   { sql: "LOWER(title) ~ 'bacchanal|bacchus|dionys|reveler|carous|silen|priap' OR (LOWER(title) ~ 'feast' AND classification IN ('Prints','Paintings','Drawings'))", weight: 3 },
+  wine:        { sql: "(LOWER(title) ~ 'wine|cup|goblet|chalice|amphora|krater|kantharos|tankard|tavern|drinking') AND classification IN ('Prints','Paintings','Drawings','Ceramics','Glass','Vases')", weight: 2 },
+  engraving:   { sql: "classification ILIKE 'Prints' AND object_begin_date BETWEEN 1700 AND 1899", weight: 1 },
+  animal:      { sql: "LOWER(title) ~ 'monkey|giraffe|elephant|orangutan|lemur|sloth|capybara|frog|macaw|parrot|peacock|owl|panda' AND classification IN ('Prints','Paintings','Drawings')", weight: 2 },
+  chinoiserie: { sql: "(department = 'Asian Art' AND LOWER(medium) ~ 'silk|cloth|textile') OR LOWER(title) ~ 'chinoiserie|wallpaper'", weight: 1 },
+};
+
+// In-memory cache. Key = theme name → array of Met rows.
+const METROW_CACHE = {};
+let MET_CACHE_AT = 0;
+const TTL_SEC = parseInt(process.env.DESIGN_REFS_TTL_SEC || '21600', 10); // 6 hr
+
+// Cap per-theme pool size to keep memory + query cost bounded.
+const POOL_SIZE = parseInt(process.env.DESIGN_REFS_POOL_SIZE || '120', 10);
+
+function psqlJson(sql) {
+  try {
+    const cmd = `psql dw_unified -At -F'\t' -c ${JSON.stringify(sql)}`;
+    return execSync(cmd, { encoding: 'utf8', timeout: 12000 });
+  } catch (e) {
+    return '';
+  }
+}
+
+function refreshMetCache() {
+  const now = Date.now();
+  if (now - MET_CACHE_AT < TTL_SEC * 1000 && Object.keys(METROW_CACHE).length) return;
+  for (const [theme, cfg] of Object.entries(MET_THEMES)) {
+    const sql =
+      `SELECT object_id, title, COALESCE(artist,'Unknown') AS artist, ` +
+      `COALESCE(NULLIF(date_display,''), object_date, 'date unknown') AS date_display, ` +
+      `COALESCE(NULLIF(classification,''), 'Artwork') AS classification, ` +
+      `COALESCE(NULLIF(met_url,''), 'https://www.metmuseum.org/art/collection/search/' || object_id) AS met_url, ` +
+      `COALESCE(NULLIF(primary_image_small,''), NULLIF(image_url,''), NULLIF(primary_image,'')) AS image ` +
+      `FROM met_museum_art ` +
+      `WHERE is_public_domain AND active AND (${cfg.sql}) ` +
+      `ORDER BY random() LIMIT ${POOL_SIZE};`;
+    const raw = psqlJson(sql);
+    const rows = raw.split('\n').filter(Boolean).map(line => {
+      const [object_id, title, artist, date_display, classification, met_url, image] = line.split('\t');
+      return {
+        source: 'met',
+        theme,
+        object_id,
+        title: (title || '').slice(0, 120),
+        artist: (artist || 'Unknown').split('|')[0].slice(0, 80),
+        date_display: (date_display || '').slice(0, 40),
+        classification: classification || '',
+        met_url, image,
+      };
+    });
+    METROW_CACHE[theme] = rows;
+  }
+  MET_CACHE_AT = now;
+}
+
+// Fallback list — used only when PG is unreachable.
+const MET_FALLBACK = [
+  { source: 'met', theme: 'bacchanal', object_id: '345721', title: 'A Bacchanal (Silenus & bacchants)', artist: 'Marcantonio Raimondi', date_display: 'ca. 1510–13', classification: 'Prints' },
+  { source: 'met', theme: 'wine',      object_id: '436106', title: 'Still Life with Wine Bottle and Apples', artist: 'Henri Fantin-Latour', date_display: '1876', classification: 'Paintings' },
+  { source: 'met', theme: 'engraving', object_id: '720507', title: 'Autumn (Wine Harvest)', artist: 'Aignan Thomas Desfriches', date_display: '1768', classification: 'Prints' },
+  { source: 'met', theme: 'animal',    object_id: '435716', title: 'Studies of Monkeys', artist: 'Antoine Watteau', date_display: 'ca. 1714–16', classification: 'Drawings' },
+];
+
+function pickRand(arr) { return arr[Math.floor(Math.random() * arr.length)]; }
+
+function compactCitation(row) {
+  if (!row) return null;
+  const yr = row.date_display ? `, ${row.date_display}` : '';
+  return `the Met's "${row.title}" (${row.artist}${yr})`;
+}
+
+/**
+ * Pick a Met Museum PD reference matching one or more themes.
+ * @param {string[]} themes  e.g. ['bacchanal', 'wine']. Defaults to all.
+ * @returns {object} { theme, title, artist, date_display, met_url, compactCitation }
+ */
+function pickMetReference(themes) {
+  refreshMetCache();
+  const pool = [];
+  const want = (themes && themes.length) ? themes : Object.keys(MET_THEMES);
+  for (const t of want) {
+    const rows = METROW_CACHE[t] || [];
+    const weight = (MET_THEMES[t] && MET_THEMES[t].weight) || 1;
+    for (let i = 0; i < weight; i++) pool.push(...rows);
+  }
+  const row = pool.length ? pickRand(pool) : pickRand(MET_FALLBACK);
+  return { ...row, compactCitation: compactCitation(row) };
+}
+
+// ── Prior wallco designs from PG (composition reference) ──────────────
+const PRIOR_CACHE = {};
+let PRIOR_CACHE_AT = 0;
+
+function refreshPriorCache() {
+  const now = Date.now();
+  if (now - PRIOR_CACHE_AT < TTL_SEC * 1000 && Object.keys(PRIOR_CACHE).length) return;
+  // Pull the top-rated published prior designs per category (stars desc),
+  // then random within ties.
+  const sql =
+    `SELECT id, ` +
+    `CASE WHEN category LIKE 'drunk-zoo-36%' THEN 'drunk-zoo-36' ELSE category END AS bucket, ` +
+    `COALESCE(LEFT(prompt, 160), '') AS prompt_head, ` +
+    `COALESCE(user_stars, 0) AS stars, ` +
+    `COALESCE(dominant_hex, '#888888') AS dominant_hex ` +
+    `FROM spoon_all_designs ` +
+    `WHERE is_published AND (category IN ('drunk-animals','drunk-monkeys-v2','drunk-animals-designer') ` +
+    `OR category LIKE 'drunk-zoo-36%') ` +
+    `ORDER BY stars DESC NULLS LAST, random() LIMIT 600;`;
+  const raw = psqlJson(sql);
+  const buckets = {};
+  for (const line of raw.split('\n').filter(Boolean)) {
+    const [id, bucket, prompt_head, stars, dominant_hex] = line.split('\t');
+    if (!buckets[bucket]) buckets[bucket] = [];
+    buckets[bucket].push({
+      source: 'prior',
+      id: parseInt(id, 10),
+      category: bucket,
+      prompt_head: (prompt_head || '').slice(0, 160),
+      stars: parseInt(stars, 10) || 0,
+      dominant_hex,
+    });
+  }
+  Object.assign(PRIOR_CACHE, buckets);
+  PRIOR_CACHE_AT = now;
+}
+
+function pickPriorDesignReference(category) {
+  refreshPriorCache();
+  const bucket =
+    (category && category.startsWith('drunk-zoo-36')) ? 'drunk-zoo-36' :
+    category || 'drunk-animals';
+  const rows = PRIOR_CACHE[bucket] || PRIOR_CACHE['drunk-animals'] || [];
+  if (!rows.length) return null;
+  const row = pickRand(rows);
+  return {
+    ...row,
+    compactCitation: `composition lineage from wallco internal design #${row.id} (${row.dominant_hex} dominant)`,
+  };
+}
+
+// ── Combined helper — return both refs ready-to-paste into a prompt ──
+function pickReferences(category) {
+  const m = pickMetReference(themesForCategory(category));
+  const p = pickPriorDesignReference(category);
+  return {
+    met: m,
+    prior: p,
+    promptClause: [
+      m && `inspired by ${m.compactCitation}`,
+      p && p.compactCitation,
+    ].filter(Boolean).join(', '),
+  };
+}
+
+function themesForCategory(category) {
+  // Map each drunk-animal sub-category to a sensible Met theme mix.
+  if (!category) return ['bacchanal', 'wine', 'animal'];
+  if (category.startsWith('drunk-zoo-36'))     return ['bacchanal', 'animal', 'engraving'];
+  if (category === 'drunk-monkeys-v2')         return ['animal', 'bacchanal'];
+  if (category === 'drunk-animals-designer')   return ['chinoiserie', 'wine', 'engraving'];
+  return ['bacchanal', 'wine', 'animal'];
+}
+
+module.exports = {
+  pickMetReference,
+  pickPriorDesignReference,
+  pickReferences,
+  themesForCategory,
+  MET_THEMES,
+};
+
+// CLI smoke-test
+if (require.main === module) {
+  console.log('Met cache warming...');
+  refreshMetCache();
+  for (const t of Object.keys(MET_THEMES)) {
+    console.log(`  ${t}: ${(METROW_CACHE[t]||[]).length} rows`);
+  }
+  console.log('\nPrior cache warming...');
+  refreshPriorCache();
+  for (const b of Object.keys(PRIOR_CACHE)) {
+    console.log(`  ${b}: ${PRIOR_CACHE[b].length} rows`);
+  }
+  console.log('\nSample reference pairs:');
+  for (const cat of ['drunk-animals', 'drunk-zoo-36', 'drunk-monkeys-v2', 'drunk-animals-designer']) {
+    const r = pickReferences(cat);
+    console.log(`\n  [${cat}]`);
+    console.log(`    met:   ${r.met && r.met.compactCitation}`);
+    console.log(`    prior: ${r.prior && r.prior.compactCitation}`);
+    console.log(`    clause: ${r.promptClause}`);
+  }
+}
diff --git a/scripts/da4-preview-batch.js b/scripts/da4-preview-batch.js
new file mode 100644
index 0000000..c002279
--- /dev/null
+++ b/scripts/da4-preview-batch.js
@@ -0,0 +1,126 @@
+#!/usr/bin/env node
+/**
+ * DA-4: heterogeneous preview batch for the drunk-animals refresh.
+ * Generates 6 designs spanning 4 categories, curator-mode (is_published=
+ * false because generate_designs.js inserts FALSE by default), local
+ * ComfyUI backend ($0). Writes data/logs/drunk-animals-preview-batch.json.
+ *
+ * Settlement-gate runs inline via scripts/settlement_tick_guard.js, so
+ * BLOCK / NEEDS_REVIEW will skip that prompt and try another.
+ */
+'use strict';
+const path = require('path');
+const fs = require('fs');
+const { spawnSync, execSync } = require('child_process');
+
+const ROOT = path.join(__dirname, '..');
+const OUT_JSON = path.join(ROOT, 'data', 'logs', 'drunk-animals-preview-batch.json');
+
+const { buildN: buildDA } = require('./drunk_animal_prompts');
+const { buildN: buildV2 } = require('./drunk_monkeys_v2_prompts');
+const { buildN: buildSub } = require('./drunk_monkeys_birds_subdued_prompts');
+const SHARED = require('./drunk_motif_shared');
+
+// Build a drunk-zoo-36 prompt by lifting ZOO_VIGNETTES from night-builder
+// (it's not exported there) and SHARED + composing it here.
+const NB = fs.readFileSync(path.join(ROOT, 'scripts', 'night-builder.js'), 'utf8');
+const VIGS_MATCH = NB.match(/const ZOO_VIGNETTES = \[([\s\S]*?)\];/);
+const VIGS = VIGS_MATCH[1].split(/'\s*,\s*\n\s*'/).map(s => s.replace(/^[\s']*|['\s,]*$/g, ''));
+const ARTDIR_MATCH = NB.match(/const SHARED_ARTDIR = ([\s\S]*?);\s*\n/);
+const SHARED_ARTDIR = ARTDIR_MATCH ? eval(ARTDIR_MATCH[1]) : '';
+
+const ZOO_CWS = [
+  { name: 'Bordeaux Velvet',  tone: 'deep burgundy',     hex: '#5a1818' },
+  { name: 'Forest Loden',     tone: 'dark gallery green', hex: '#22443a' },
+];
+
+function composeDrunkZoo36(cw) {
+  const vig = VIGS[Math.floor(Math.random() * VIGS.length)];
+  const tex = SHARED.TEXTURE_GROUNDS[Math.floor(Math.random() * SHARED.TEXTURE_GROUNDS.length)];
+  const bb  = SHARED.BOTTLE_BACKDROPS[Math.floor(Math.random() * SHARED.BOTTLE_BACKDROPS.length)];
+  const ref = SHARED.pickReferenceClause('drunk-zoo-36');
+  return (
+    `Drunk Zoo 36" repeat wallpaper — ${cw.name} colorway. ` +
+    `Substrate: real ${tex.slug} (${tex.voice.replace(/^rendered on /, '')}). ` +
+    `Ground: solid ${cw.tone} (${cw.hex}) tinted over the natural-fiber substrate so the texture grain still reads through. ` +
+    `Figure: bone-ivory (#F2EADB) single-ink. ` +
+    `Vignette: ${vig}. Renders at wall-scale 36-inch seamless tile — motifs are LARGE, anchored on the central vertical axis of symmetry, generous breathing room of clean ground around each motif. ` +
+    (ref ? `Reference anchor: ${ref}. ` : '') +
+    `Secondary detail in negative space: ${bb} (rendered as a tiny bone-ivory ink silhouette at the very back, smaller scale than the main pair, not competing). ` +
+    `MANDATORY motifs visible in EVERY vignette: (1) alcohol being consumed, (2) a lit handrolled cannabis joint with a visible curl of smoke, (3) the animal(s) swinging from something. ` +
+    `Each animal is one FLAT STENCIL SILHOUETTE in bone-ivory ink — no interior shading. The joint and smoke wisp are tiny silhouette props inside the bone-ivory ink layer. NO cannabis leaf, NO marijuana leaf, NO foliage. ` +
+    `Whimsical playful joyful tone. Lean toward BOTANICALS over leaves. ` +
+    SHARED_ARTDIR
+  );
+}
+
+// Compose the 6 prompts
+const batch = [
+  { cat: 'drunk-animals',          prompt: buildDA(1)[0] },
+  { cat: 'drunk-animals',          prompt: buildDA(1)[0] },
+  { cat: 'drunk-zoo-36',           prompt: composeDrunkZoo36(ZOO_CWS[0]) },
+  { cat: 'drunk-zoo-36',           prompt: composeDrunkZoo36(ZOO_CWS[1]) },
+  { cat: 'drunk-monkeys-v2',       prompt: buildV2(1)[0].prompt },
+  { cat: 'drunk-animals-designer', prompt: buildSub(1)[0].prompt },
+];
+
+function psqlMaxId() {
+  try {
+    return parseInt(execSync(`psql dw_unified -At -c "SELECT COALESCE(max(id),0) FROM spoon_all_designs;"`, { encoding: 'utf8' }).trim(), 10) || 0;
+  } catch { return 0; }
+}
+
+const results = [];
+const startedAt = new Date().toISOString();
+
+for (let i = 0; i < batch.length; i++) {
+  const { cat, prompt } = batch[i];
+  console.log(`\n[${i+1}/${batch.length}] cat=${cat} — kicking off generate_designs.js`);
+  const maxBefore = psqlMaxId();
+  const t0 = Date.now();
+  const r = spawnSync('node', [
+    path.join(ROOT, 'scripts', 'generate_designs.js'),
+    '--n', '1',
+    '--kind', 'seamless_tile',
+    '--category', cat,
+    '--prompts', prompt,
+  ], { stdio: 'inherit', cwd: ROOT, timeout: 5 * 60_000 });
+
+  const elapsedSec = ((Date.now() - t0) / 1000).toFixed(1);
+  let newId = null;
+  try {
+    const raw = execSync(
+      `psql dw_unified -At -c "SELECT id FROM spoon_all_designs WHERE category='${cat.replace(/'/g, "''")}' AND id > ${maxBefore} ORDER BY id DESC LIMIT 1;"`,
+      { encoding: 'utf8' }).trim();
+    newId = raw ? parseInt(raw, 10) : null;
+  } catch {}
+
+  results.push({
+    idx: i + 1,
+    category: cat,
+    new_design_id: newId,
+    elapsed_sec: parseFloat(elapsedSec),
+    exit_status: r.status,
+    view_url: newId ? `http://127.0.0.1:9792/design/${newId}` : null,
+    image_url: newId ? `http://127.0.0.1:9792/designs/img/by-id/${newId}` : null,
+    prompt_head: prompt.slice(0, 200),
+  });
+
+  console.log(`  → ${newId ? `id=${newId} (${elapsedSec}s)` : 'NO NEW DESIGN'}`);
+}
+
+const out = {
+  generated_at: startedAt,
+  finished_at: new Date().toISOString(),
+  total_designs: results.length,
+  successful: results.filter(r => r.new_design_id).length,
+  results,
+};
+
+fs.mkdirSync(path.dirname(OUT_JSON), { recursive: true });
+fs.writeFileSync(OUT_JSON, JSON.stringify(out, null, 2));
+console.log(`\n✓ wrote ${OUT_JSON}`);
+console.log(`  ${out.successful} / ${out.total_designs} generated successfully`);
+for (const r of results) {
+  console.log(`  ${r.category}\tid=${r.new_design_id}\t${r.view_url || '(none)'}`);
+}
diff --git a/scripts/drunk_animal_prompts.js b/scripts/drunk_animal_prompts.js
index a22b249..e45f7b9 100644
--- a/scripts/drunk_animal_prompts.js
+++ b/scripts/drunk_animal_prompts.js
@@ -319,6 +319,7 @@ function buildPrompt(seed = null) {
       TONE_ON_TONE_SUFFIX
     ].join(', ');
   }
+  const refClause = SHARED.pickReferenceClause('drunk-animals');
   return [
     pickRand(ANIMALS), pickRand(TABLEAUX),
     // Mandatory motifs — Steve 2026-05-25
@@ -326,12 +327,13 @@ function buildPrompt(seed = null) {
     pickRand(SWINGS),
     pickRand(BOTTLE_BACKDROPS),
     pickTextureGroundVoice(),
+    refClause && `${refClause}`,
     `set among ${pickRand(BOTANICALS)}`,
     `palette: ${pickRand(pickPaletteSource())}`,
     pickRand(STYLES),
     pickRand(COMPOSITIONS),
     SHARED.DRUNK_NEGATIVE_SUFFIX
-  ].join(', ');
+  ].filter(Boolean).join(', ');
 }
 
 function buildN(n) {
@@ -344,17 +346,19 @@ function buildN(n) {
     if (usedStyles.size >= STYLES.length - 1) usedStyles.clear();
     const animal = pickRand(ANIMALS, usedAnimals);  usedAnimals.add(animal);
     const style  = pickRand(STYLES, usedStyles);    usedStyles.add(style);
+    const refClause = SHARED.pickReferenceClause('drunk-animals');
     const parts = [
       animal,
       pickRand(TABLEAUX),
       // Mandatory motifs — Steve 2026-05-25 — every drunk-animal must show
       // alcohol-in-hand + lit joint + swinging + wild-bottle backdrop +
       // rendered on a natural-fiber texture ground (grasscloth/sisal/raffia/
-      // Madagascar etc.).
+      // Madagascar etc.) + Met PD + prior wallco design reference clause.
       pickRand(JOINTS),
       pickRand(SWINGS),
       pickRand(BOTTLE_BACKDROPS),
       pickTextureGroundVoice(),
+      refClause,
       `set among ${pickRand(BOTANICALS)}`,
       `palette: ${pickRand(pickPaletteSource())}`,
       style,
@@ -362,7 +366,7 @@ function buildN(n) {
       SHARED.DRUNK_NEGATIVE_SUFFIX,
       // Aesthetic gate — Steve 2026-05-19: empirical professional palette from dw_unified
       TONE_ON_TONE_SUFFIX
-    ];
+    ].filter(Boolean);
     out.push(parts.join(', '));
   }
   return out;
diff --git a/scripts/drunk_monkeys_birds_subdued_prompts.js b/scripts/drunk_monkeys_birds_subdued_prompts.js
index 21450b3..c8809e2 100644
--- a/scripts/drunk_monkeys_birds_subdued_prompts.js
+++ b/scripts/drunk_monkeys_birds_subdued_prompts.js
@@ -174,14 +174,17 @@ function buildOne(i) {
     ? { name: 'deep-tone-on-tone', voice: pick(SHARED.DEFAULT_PALETTES), hex: [] }
     : pick(PALETTES);
 
+  const refClause = SHARED.pickReferenceClause('drunk-animals-designer');
+
   // Build the prompt — clearly SUBDUED, clearly REPEAT-FRIENDLY, clearly
   // settlement-safe (no directional/tropical foliage, controlled multicolor).
   // Steve 2026-05-25 — mandatory drink + joint + swinging + bottle-backdrop
-  // + natural-fiber texture in every prompt.
+  // + natural-fiber texture + Met-PD reference in every prompt.
   const positive =
     `Editorial wallpaper design — ${animal}, ${tableau}, also holding ${joint}, ${swing}, ` +
     `${bottleBackdrop}, ` +
     `${texture.voice}, ` +
+    (refClause ? `reference anchor: ${refClause}, ` : '') +
     `set on a subdued botanical ground of ${botanical}. ` +
     `Composition: ${composition}. ` +
     `Palette: ${palette.voice}${palette.hex && palette.hex.length ? '; palette hex anchors: ' + palette.hex.join(', ') : ''}. ` +
diff --git a/scripts/drunk_monkeys_v2_prompts.js b/scripts/drunk_monkeys_v2_prompts.js
index bb6214e..53e864b 100644
--- a/scripts/drunk_monkeys_v2_prompts.js
+++ b/scripts/drunk_monkeys_v2_prompts.js
@@ -158,13 +158,16 @@ function buildOne(i) {
     ? { name: 'deep-tone-on-tone', voice: pick(SHARED.DEFAULT_PALETTES) }
     : pick(PALETTES);
 
-  // SUBJECT FIRST — that's the key v2 fix. ~250 char prompt total.
-  // Steve 2026-05-25 — mandatory joint + swinging + bottle-backdrop +
-  // natural-fiber texture in every prompt.
+  const refClause = SHARED.pickReferenceClause('drunk-monkeys-v2');
+
+  // SUBJECT FIRST — that's the key v2 fix. Steve 2026-05-25 — mandatory
+  // joint + swinging + bottle-backdrop + natural-fiber texture + Met-PD
+  // reference clause in every prompt.
   const positive =
     `${subject}, ${action}, also ${joint}, ${swing}. ` +
     `${bottleBackdrop}. ` +
     `${texture.voice}. ` +
+    (refClause ? `Reference anchor: ${refClause}. ` : '') +
     `${backdrop}. ` +
     `Hand-painted gouache wallpaper illustration, ${composition}, ${palette.voice}, ` +
     `restrained tone-on-tone, museum-archival print quality. ` +
diff --git a/scripts/drunk_motif_shared.js b/scripts/drunk_motif_shared.js
index 7d5672e..c4baf04 100644
--- a/scripts/drunk_motif_shared.js
+++ b/scripts/drunk_motif_shared.js
@@ -23,6 +23,23 @@
 const path = require('path');
 const fs = require('fs');
 
+// Met PD + prior wallco design reference picker — lib/design-references.js
+// is loaded lazily so this module stays cheap to require() in cron paths.
+let _refs = null;
+function getRefs() {
+  if (_refs === null) {
+    try { _refs = require('../lib/design-references'); }
+    catch { _refs = { pickReferences: () => ({ promptClause: '', met: null, prior: null }) }; }
+  }
+  return _refs;
+}
+function pickReferenceClause(category) {
+  try {
+    const r = getRefs().pickReferences(category);
+    return r && r.promptClause ? r.promptClause : '';
+  } catch { return ''; }
+}
+
 // ── JOINTS — lit handrolled cannabis joints w/ smoke trail ─────────────
 // Small silhouette prop, NEVER a cannabis leaf. The negative prompt
 // explicitly bans "cannabis leaf / marijuana leaf" so the joint stays
@@ -252,14 +269,15 @@ function pickRand(arr, exclude) {
 
 // One-line mandatory motif block — drop-in for any prompt generator that
 // wants the guaranteed alcohol+joint+swing+bottle-backdrop+texture clause.
-function buildMandatoryMotifs() {
+function buildMandatoryMotifs(category) {
   return {
-    drink:          pickRand(TABLEAUX),
-    joint:          pickRand(JOINTS),
-    swing:          pickRand(SWINGS),
-    bottleBackdrop: pickRand(BOTTLE_BACKDROPS),
-    texture:        pickRand(TEXTURE_GROUNDS),
-    palette:        pickRand(DEFAULT_PALETTES),
+    drink:           pickRand(TABLEAUX),
+    joint:           pickRand(JOINTS),
+    swing:           pickRand(SWINGS),
+    bottleBackdrop:  pickRand(BOTTLE_BACKDROPS),
+    texture:         pickRand(TEXTURE_GROUNDS),
+    palette:         pickRand(DEFAULT_PALETTES),
+    referenceClause: pickReferenceClause(category),
   };
 }
 
@@ -277,7 +295,7 @@ const DRUNK_NEGATIVE_SUFFIX =
 module.exports = {
   JOINTS, SWINGS, TABLEAUX, BOTTLE_BACKDROPS, TEXTURE_GROUNDS,
   FASHION_PALETTES, TONE_ON_TONE_DEEP_PALETTES, DEFAULT_PALETTES,
-  pickRand, buildMandatoryMotifs,
+  pickRand, pickReferenceClause, buildMandatoryMotifs,
   DRUNK_NEGATIVE_SUFFIX,
 };
 
diff --git a/scripts/night-builder.js b/scripts/night-builder.js
index 2633d47..cca050c 100755
--- a/scripts/night-builder.js
+++ b/scripts/night-builder.js
@@ -403,12 +403,14 @@ function composeDrunkZoo36Prompt(cw) {
   // negative space, in the same bone-ivory ink as the main silhouettes.
   const tex = SHARED.TEXTURE_GROUNDS[Math.floor(Math.random() * SHARED.TEXTURE_GROUNDS.length)];
   const bottleBackdrop = SHARED.BOTTLE_BACKDROPS[Math.floor(Math.random() * SHARED.BOTTLE_BACKDROPS.length)];
+  const refClause = SHARED.pickReferenceClause('drunk-zoo-36');
   return (
     `Drunk Zoo 36" repeat wallpaper — ${cw.name} colorway. ` +
     `Substrate: real ${tex.slug} (${tex.voice.replace(/^rendered on /, '')}). ` +
     `Ground: solid ${cw.tone} (${cw.hex}) tinted over the natural-fiber substrate so the texture grain still reads through. ` +
     `Figure: bone-ivory (#F2EADB) single-ink. ` +
     `Vignette: ${vig}. Renders at wall-scale 36-inch seamless tile — motifs are LARGE, anchored on the central vertical axis of symmetry, generous breathing room of clean ground around each motif. ` +
+    (refClause ? `Reference anchor: ${refClause}. ` : '') +
     `Secondary detail in negative space: ${bottleBackdrop} (rendered as a tiny bone-ivory ink silhouette at the very back, smaller scale than the main pair, not competing). ` +
     `MANDATORY motifs visible in EVERY vignette: (1) alcohol being consumed (drink glass or bottle held to mouth/in paw), (2) a lit handrolled cannabis joint with a visible curl of smoke, (3) the animal(s) swinging from something — vine, branch, chandelier, rope, or porch swing — mid-swing arc visible. ` +
     `Each animal is one FLAT STENCIL SILHOUETTE in bone-ivory ink — no interior shading, no anatomical line-detail, no spots, no stripes, no fur texture. Spots/stripes appear ONLY as cut-out negative shapes in the silhouette (revealing ground tone), never as a second ink color. The joint and smoke wisp are tiny silhouette props inside the bone-ivory ink layer (joint = small rolled-paper cylinder with a glowing tip; smoke = a thin curling line). NO cannabis leaf, NO marijuana leaf, NO foliage of any kind. ` +

← 5f63e2d remove mural pan HUD (5 arrows ↑↓←→⟲) + drag-to-pan on /desi  ·  back to Wallco Ai  ·  design-coordinate cards: image-only grid, hover overlay reve 0e37522 →