← back to Wallco Ai
scripts/bad-examples-schema.sql
63 lines
-- bad_examples — permanent, isolated anti-pattern registry for wallco.ai.
-- "Keep all bad/deleted items in its own db so we never create the same error."
-- The negative mirror of the `elements` (good-reference) library.
--
-- Design rules:
-- * ONE row per design_id. defect_tags accumulates every defect signal.
-- * Stores the PROVENANCE that produced the error (prompt / negative_prompt /
-- seed / generator / category / dims) so generation can learn the anti-pattern.
-- * local_path is preserved — the bad FILE is never deleted; this row points to it.
-- * Lives in dw_unified but is NEVER written to data/designs.json, so catalog
-- rebuilds / publishes cannot clobber it. Append-and-merge only.
CREATE TABLE IF NOT EXISTS bad_examples (
design_id BIGINT PRIMARY KEY,
defect_tags TEXT[] NOT NULL DEFAULT '{}', -- {edges_fail, user_removed, ghost, bleed, heal_derivative, repeat_break_lr, repeat_break_tb, ...}
severity INT, -- worst known severity 0-3
category TEXT,
prompt TEXT, -- the prompt that produced the bad design
negative_prompt TEXT,
seed BIGINT,
generator TEXT, -- replicate / comfy / pil-mid-heal / gemini-...
width_in NUMERIC,
height_in NUMERIC,
dominant_hex TEXT,
parent_design_id BIGINT,
local_path TEXT, -- preserved bad file (NEVER deleted)
scores JSONB, -- lens / structural scores when available
notes TEXT, -- original defect detail
source TEXT, -- backfill | repeat-break-scan | agent-audit | bulk-reject | edges-agent
design_created_at TIMESTAMPTZ,
recorded_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX IF NOT EXISTS bad_examples_defect_idx ON bad_examples USING GIN(defect_tags);
CREATE INDEX IF NOT EXISTS bad_examples_seed_idx ON bad_examples(seed);
CREATE INDEX IF NOT EXISTS bad_examples_cat_idx ON bad_examples(category);
CREATE INDEX IF NOT EXISTS bad_examples_gen_idx ON bad_examples(generator);
-- Backfill from every currently-bad-or-deleted design, merging defect tags.
INSERT INTO bad_examples
(design_id, defect_tags, category, prompt, negative_prompt, seed, generator,
width_in, height_in, dominant_hex, parent_design_id, local_path, notes, source, design_created_at)
SELECT id,
( ARRAY[]::text[]
|| CASE WHEN user_removed THEN ARRAY['user_removed'] ELSE '{}'::text[] END
|| CASE WHEN notes ILIKE '%EDGES_FAIL%' THEN ARRAY['edges_fail'] ELSE '{}'::text[] END
|| CASE WHEN notes ILIKE '%EDGES_SCAN_ERROR%' THEN ARRAY['edges_scan_error'] ELSE '{}'::text[] END
|| CASE WHEN notes ILIKE '%ghost%' THEN ARRAY['ghost'] ELSE '{}'::text[] END
|| CASE WHEN notes ILIKE '%bleed%' THEN ARRAY['bleed'] ELSE '{}'::text[] END
|| CASE WHEN notes ILIKE '%mid_heal%' OR notes ILIKE '%MID_HEALED%' OR generator ILIKE '%heal%'
THEN ARRAY['heal_derivative'] ELSE '{}'::text[] END
),
category, prompt, negative_prompt, seed, generator,
width_in, height_in, dominant_hex, parent_design_id, local_path, notes, 'backfill', created_at
FROM spoon_all_designs
WHERE user_removed = TRUE
OR notes ILIKE '%EDGES_FAIL%' OR notes ILIKE '%EDGES_SCAN_ERROR%'
OR notes ILIKE '%ghost%' OR notes ILIKE '%bleed%'
OR notes ILIKE '%mid_heal%' OR notes ILIKE '%MID_HEALED%' OR generator ILIKE '%heal%'
ON CONFLICT (design_id) DO UPDATE
SET defect_tags = (SELECT array_agg(DISTINCT t)
FROM unnest(bad_examples.defect_tags || EXCLUDED.defect_tags) AS t),
recorded_at = now();