1fb96442026-06-10new-import-viewer: enforce 3-source NEVER-DUPLICATE in net-new predicate + hard server-side stage gate
7be62072026-06-10index.html review fixes: map Shopify lowercase status (active/draft/archived) → app vocabulary after live action (chip styled + Publish button disables correctly); show staged-so-far count in bulk error; only emit vendor link for http(s) urls (block javascript:/data: from scraped p.url)
24a8d832026-06-10launch-queue CONSUMER: manifested batches → Shopify draft products. DRY-RUN default; live needs LAUNCH_CONSUMER_LIVE=1 + --live (Steve gate). Per-product execution gates: image required, dw_sku+pattern required, fresh NEVER-DUPLICATE (mfr_sku vs shopify_products + in-run dedup + linked check), vendor_registry skip_shopify + private-label display names, cowtan denylist, status forced draft, 450/run hard cap (daily variant limit), 600ms throttle + 429 retry, idempotent ledger resume; writes linkage back to vendor_catalog on create
863ce652026-06-10staging manifests: every batch persists full id/SKU manifest to data/launch-batches/ (queue lines were counts-only = un-actionable); superseded pre-gate count-only batches via queue marker; restaged ALL netnew gated: 120,991 staged with manifests, 8,221 no-image BLOCKED, cowtan_tout (8,312) excluded per Steve 'never'
4fb4e882026-06-10HARD GATE: all products must have images (Steve 2026-06-10) — launch staging filters no-image rows server-side (single, bulk, raw API); launch buttons disabled on no-image cards with reason; bulk UI reports blocked count; non-launch actions unaffected
01963b92026-06-10harden: noimage boot-trap defused (persisted diagnostic filter falls back to netnew) + /img local thumbnail proxy with disk cache + SSRF guard — grid no longer depends on 100+ vendor hosts hotlink policy
cd997292026-06-10stage net-new for ALL vendors (101 vendors, 111,234 skus) to launch queue — draft, stage-only (no Shopify writes). Excluded: cowtan_tout (Steve 'never'), newwall+command54 (already staged). With newwall+command54 = 125,950 total net-new queued. NEVER-DUPLICATE: all pre-filtered against Shopify mfr_sku
a4c7ca52026-06-09stage net-new for newwall (8485) + command54 (6231) to launch queue — draft mode, stage-only (no Shopify writes). command54 carries private-label rule (Phillipe Romano relabel at import)
58e24b22026-06-09NET NEW by vendor panel: per-vendor breakdown (count + image coverage withImg/noImg) shown for netnew/new status, click-to-filter, image-% bar per vendor. /api/vendors now returns image coverage. Surfaces which vendors have importable net-new WITH images (e.g. hollywood 8997 100%, brewster_york 7772 94%)
bde393a2026-06-09NET NEW view: new MINUS already-on-Shopify (Steve: 'new - on shopify = new not on shopify'). NETNEW_PRED excludes any flagged-new row whose mfr pattern number already exists in shopify_products (index-friendly equality, 240ms). New default status + header pill + health-strip chip + dup-overlap warning. Found 62,030 'new' rows are actually already on Shopify -> true net-new 134,247 not 196,277
18337352026-06-09Full action suite per chip + select 1→all: every card renders all 5 actions (disabled-with-reason; launch hard-disabled on linked rows = dup guard) + copy SKU/DW + vendor/admin/storefront links; visible 🕓 crawl date+time on grid cards; /api/ids endpoint + first-N / shown / ALL-matching selection; bulk staging chunks through the 5k cap (includes parallel-session netnew predicate)
fe11c1f2026-06-09Panel item A: split details/actions into separate triggers — ⓘ details (read-only) vs ⚙ actions (write surface); browsing metadata never reveals a live button; actions chip reddens when armed
e4e0ced2026-06-09DTD-C: per-session arm toggle defaults OFF every load; live write now needs .env ceiling AND session-armed (arm:true). .env stays hard ceiling, bulk still stage-only
0d6de3d2026-06-09Apply design-agent critique: red danger treatment for live action buttons + armed top strip + pulsing LIVE badge, de-shout vendor labels, title hierarchy, two-column spec drawer
90782f62026-06-09Refine: bulk forced stage-only (live single-card only), in-place chip+action refresh after live write, accurate live-vs-staged confirms, live result detail
16d4ca82026-06-09Force live-write gate OFF by default (SHOPIFY_LIVE_WRITES master switch, stage-only); restore aligned CSS-grid list view; clear test launch-queue lines
b2cb29b2026-06-09Wire live Shopify unpublish behind double gate (master switch + per-action allowlist); fix admin-link store handle to designer-laboratory-sandbox
d24c5ab2026-06-09Show full Shopify state (new/published/unpublished/archived) + staged action suite under collapsed per-card chips; indexed shopify_products join
af2b0772026-06-09snapshot before shopify-status + collapsed-chips rebuild
186111c2026-06-09viewer: add List view toggle (compact horizontal rows, persisted) alongside the card grid
039ba4a2026-06-09viewer: select-and-launch — per-product checkboxes, select-all, launch bar (draft/active) → /api/launch stages a batch (live Shopify push stays a separate gated step)
f6b13002026-06-09fix: strip psql trailing newline from final field (stats newestCrawl + per-vendor crawled)
1a10b712026-06-09new-import-viewer: agent + web viewer for products new-to-import (sync_status=new / not on Shopify)
Authors
Steve41
steve1
Agents used
none detected
Skills used
/stats4
/pattern2
/action2
/archived2
/ids2
/vendors2
/similar1
/npm1
/npx1
/homebrew1
/index1
/offset1
/desc1
/collection1
/type1
/sync1
/shopid1
/status1
/color1
/sku1
/page1
/shopify1
/batch1
/never1
/pile1
/live1
/dtd-1
/draft1
/data1
/run1
Creative ideas + design notes
Commits with substantial prose (≥120 chars) — the rationale behind each move.
f65d9a6 · 2026-07-02 · Grid UI upgrade: sortable list-view column headers (server-side sorts), density max 20, Safari user:pass@ fetch fix
- Density slider max 10 → 20.
- List view: sticky header row of 12 sortable column labels (vendor, pattern,
color, status, vendor SKU, DW SKU, collection, type, sync, shopify id,
first seen, last crawl) sharing ONE grid-template-columns rule with the row
cards so labels align with cells by construction. Click = asc, click again =
desc, ▲/▼ indicator; new list-only cells (.lcell) per field.
- Sorting stays SERVER-side (grid paginates limit/offset — client-sorting the
loaded page would lie): extended the injection-safe SORTS whitelist in
server.js with asc/desc pairs for dwsku/collection/type/sync/shopid/status
(derived SHOP_STATUS CASE) + vendor/pattern/color/sku desc + firstseen_asc.
- Safari creds fix: all fetch('/api/…') → fetch(location.origin+'/api/…') —
relative fetches break in WebKit on pages opened with user:pass@ URLs.
- Presentational + sort wiring only; NO changes to arm-toggle / live-write /
stage-action machinery.
- E2E-verified with Playwright on local :9790 against dw_unified: slider
max=20, header row renders + computed grid template matches row cards,
vendor asc→desc reorders, DW-SKU header reorders again, sticky on scroll,
0 console/page errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
96cd954 · 2026-06-19 · consumer.js: fix publish gap (publishablePublish to Online Store) + add required Sample variant
REST status:'active' never publishes to a sales channel — products landed on
zero channels (publishedOnPublication=false). Add GraphQL publishablePublish to
the Online Store publication after each active create, plus the mandatory
{DW_SKU}-Sample $4.25 variant. Drafts stay unpublished by design.
Root cause: netnew count's NOT EXISTS vs dw_sku_registry had no functional
index (upper(trim(mfr_sku))) → seq scan → 75s+/never. Every page-load + the
boot warmer fired it; runs piled up (13 stacked 18-43min), saturating PG into
a death spiral that froze the whole single-threaded server ('and loading').
Fix (this commit): PGOPTIONS statement_timeout=30000 on q()+qAsync so no query
can ever run away/pile up again; background stats warmer + STATS_CACHE serve
/api/stats instantly.
DB side (not in git): CREATE INDEX idx_registry_mfr_upper_trim ON
dw_sku_registry (upper(trim(mfr_sku))) — drops the stats query to ~1.6s.
1fb9644 · 2026-06-10 · new-import-viewer: enforce 3-source NEVER-DUPLICATE in net-new predicate + hard server-side stage gate
NETNEW_PRED now NOT-EXISTS against dw_sku_registry as well as shopify_products
(was shopify-only — 49,386 distinct mfr_skus were shopify-clean but already in
the 84k registry → would have imported as dups). Added dedupFilterIds() that
re-validates every staged/live candidate row's mfr_sku against shopify_products
AND dw_sku_registry on upper(trim()), dropping dups + un-dedupable rows, fail-safe
(query error aborts the whole batch). Applied in /api/action before BOTH staging
and live writes so a stale id list / raw curl can never push a known dup. /dtd-A 2026-06-10.
16d4ca8 · 2026-06-09 · Force live-write gate OFF by default (SHOPIFY_LIVE_WRITES master switch, stage-only); restore aligned CSS-grid list view; clear test launch-queue lines
- /api/action stages to data/launch-queue.jsonl and never writes the store unless SHOPIFY_LIVE_WRITES=1 (default 0); .env ships gate=0, no live actions allow-listed
- List view rebuilt as aligned CSS grid (display:contents on card body) so checkbox/thumb/vendor/pattern/status-chip/SKU/crawl columns line up across rows; full crawl date+time, ~54px rows; details drawer drops full-width below
- indexed shopify_products join (gid built on vc side) keeps /api/stats sub-second
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1a10b71 · 2026-06-09 · new-import-viewer: agent + web viewer for products new-to-import (sync_status=new / not on Shopify)
Zero-dep node+psql server over dw_unified.vendor_catalog. /api/stats, /api/vendors,
/api/new-products (vendor filter + search + 7 sort modes + pagination). Viewer has
the mandatory sort select + density slider (localStorage-persisted) and the admin
crawl date+time chip per standing rules. Read-only; surfaces what the latest vendor
crawl flagged as NEW before any Phase-8 push. Port 9790.
File tree
16 files tracked. Click any to browse the source at HEAD.